由于该方法的实际编译字节码,因此没有发生演员 类型擦除.
当生成字节码时,编译器将参数类型的任何变量视为具有与该参数类型上限相同的类型,或 Object
如果类型是无限的。因此,如果 T
在您的方法中有约束 <T extends MyServelet>
, ,编译器会治疗 proxy
作为类型的变量 MyServelet
, 并插入了演员。但是,如 T
是无限的,被视为类型的变量 Object
- 因此,没有演员。
题
Imy问题涉及使用动态代理和Java仿制药的类型铸造的行为。更具体地说,我想使用动态代理来仪器一个Servlet对象。为了获得更好的可重复性,我采用了一些通用代码模板来创建以下代理对象。
@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;
}
这是我实现myServelet接口的混凝土Servlet实现类。但是,如果我运行上述方法,它将打印出来
proxy class class com.sun.proxy.$Proxy0
input class class MyServeletImplementation
因此,我想知道代码段中的类型铸造语句发生了什么。似乎悄悄地失败了,因为代理没有被铸造为MyServeletimplimentation,但也没有抛出ClassCastException。有人可以给我一些启示吗?谢谢!
解决方案
由于该方法的实际编译字节码,因此没有发生演员 类型擦除.
当生成字节码时,编译器将参数类型的任何变量视为具有与该参数类型上限相同的类型,或 Object
如果类型是无限的。因此,如果 T
在您的方法中有约束 <T extends MyServelet>
, ,编译器会治疗 proxy
作为类型的变量 MyServelet
, 并插入了演员。但是,如 T
是无限的,被视为类型的变量 Object
- 因此,没有演员。
其他提示
动态代理的目的是,您不在乎返回的代理的实际类别。您只是在乎它实现所有所需和指定的接口。
返回:
由指定类加载程序定义的代理类指定的调用处理程序的代理实例,并实现了指定的接口
所以, newProxyInstance
返回了一个实例 com.sun.proxy.$Proxy0
(无论是什么,我们真的不在乎),但它也确保这是 MyServelet
. 。你应该能够把它扔给 MyServelet
没有 ClassCastException
, , 因为 newProxyInstance
创建类以实现该界面,您指定它应该通过传递 new Class[]{MyServelet.class}
.
对于类型铸造,不要失败,Java Generics必须推断出 T
是 MyServelet
, , 不是 MyServeletImplementation
. 。也许当你打电话 instrument
, ,您这样做了:
MyServelet proxy = YourClass.instrument(new MyServeletImplementation());
所以演员是 MyServelet
哪个应该成功,而不是 MyServeletImplementation
, ,应该失败。
您需要添加的原因 @SuppressWarnings("unchecked")
是因为编译器试图警告您您做错了什么。通过添加抑制作用,您告诉编译器您不在乎,因为您知道自己在做什么。如果您没有首先获得警告,也许您不应该添加注释?
提示:通用演员 T proxy = (T)
变成 Object proxy = (Object)
由编译器。 (如另一个答案中提到的“键入擦除”)。