0

IMy 的问题涉及使用动态代理和 java 泛型进行类型转换的行为。更具体地说,我想使用动态代理来检测一个 serverlet 对象。为了更好的重用性,我采用了一些通用代码模板来创建代理对象,如下所示。

@SuppressWarnings("unchecked")
public static <T> T instrument(final T aT) {
    T proxy = (T) Proxy.newProxyInstance(aT.getClass().getClassLoader(), new Class[]{MyServelet.class}, new InvocationHandler() {
        @Override
        public Object invoke(Object aProxy, Method aMethod, Object[] aArgs) throws Throwable {
        ..... instrumentation logic goes here
        }
    });
     System.out.println("proxy class " + proxy.getClass());
     System.out.println("input class " + aT.getClass());
     return proxy;
}

这里的 T 是我实现 MyServelet 接口的具体 serverlet 实现类。但是,如果我运行上述方法,它会打印出来

proxy class class com.sun.proxy.$Proxy0
input class class MyServeletImplementation

所以我想知道代码片段中的类型转换语句发生了什么。似乎它悄悄地失败了,因为代理没有被强制转换为 MyServeletImplementation,但它也没有抛出 ClassCastException。有人可以帮我解释一下吗?谢谢!

4

3 回答 3

2

由于类型擦除,该方法的实际编译字节码中没有发生强制转换。

生成字节码时,编译器将参数类型的任何变量视为具有与该参数类型的上限相同的类型,或者Object该类型是无界的。因此,如果T在您的方法中有约束<T extends MyServelet>,编译器将被proxy视为 type 的变量MyServelet,并插入一个强制转换。但是,由于T是无界的,它被视为类型的变量Object- 因此,没有强制转换。

于 2013-11-08T00:17:27.310 回答
0

您需要添加@SuppressWarnings("unchecked")的原因是编译器试图警告您您做错了什么。通过添加抑制,您告诉编译器您不在乎,因为您知道自己在做什么。如果您不了解为什么首先收到警告,也许您不应该添加注释?

提示:泛型T proxy = (T)转换Object proxy = (Object)由编译器转换。(另一个答案中提到的“类型擦除”)。

于 2013-11-08T00:19:35.577 回答
0

动态代理的要点是您不关心返回的代理的实际类。您只关心它实现了所有需要和指定的接口。

状态的 JavadocsnewProxyInstance

回报:

具有代理类的指定调用处理程序的代理实例,该代理类由指定的类加载器定义并实现指定的接口

所以,返回了一个(不管是什么,我们并不关心)newProxyInstance的实例,但它也确保它是一个. 您应该能够在没有 的情况下将其转换为,因为创建了实现该接口的类,并且您指定它应该通过传入.com.sun.proxy.$Proxy0MyServeletMyServeletClassCastExceptionnewProxyInstancenew Class[]{MyServelet.class}

为了使类型转换没有失败,Java 泛型必须推断出T不是。也许当你打电话时,你是这样做的:MyServeletMyServeletImplementationinstrument

MyServelet proxy = YourClass.instrument(new MyServeletImplementation());

所以演员是MyServelet应该成功的,而不是MyServeletImplementation应该失败的。

于 2013-11-08T00:16:41.073 回答