我有一个通用的方法,我需要获取参数的类:
public static<S> void doSomething(S inst) {
@SuppressWarnings("unchecked")
Class<? extends S> cls = (Class<? extends S>) inst.getClass();
// do something with 'cls' and with 'inst'...
}
事实证明,从编译器的类型系统的角度来看,.getClass()
会返回Class<?>
因此向下转换为Class<? extends S>
,这是一个未选中 dow-cast。
我的问题如下:在什么情况下,这种失败是不安全的?
IIUC,此方法的所有调用站点必须传递inst
参数的值,该参数是静态(编译时)类型S.值的动态(运行时)类型D必须是S或其子类型。因此,inst.getClass()将返回Class<D>
对象,假定D
是[{1}}的子类型,则该对象应该可以赋予S
。
答案 0 :(得分:1)
我不确定它是否会对你有所帮助,但我试图找到一个示例情况,因为这个类演员可能很危险。
public static <S> void doSomething(S inst, Consumer<S> newInstConsumer) throws IllegalAccessException, InstantiationException {
inst = (S) new Object(); // just a line of wrong code here
Class<? extends S> cls = (Class<? extends S>) inst.getClass(); // and if we don't check here
S newInstance = cls.newInstance();
newInstConsumer.accept(newInstance); // this code fails only here
}
@SuppressWarnings("unchecked")
public static void main(String... args) throws InstantiationException, IllegalAccessException {
doSomething(new HelloWorldPrinter(), HelloWorldPrinter::print);
}
答案 1 :(得分:1)
在什么情况下,这种下线是不安全的?
inst.getClass()
的返回类型为Class<? extends |X|>
,其中|X|
是表达式inst
的编译时类型的擦除。在这里,|X|
是S
的删除,即Object
。这就是为什么它是Class<?>
而不是Class<? extends S>
。
必须删除的原因是S
可能是参数化类型,但只应在Class
的类型参数中使用已知类型,仅作为Class
个对象代表具体类型。例如,您可以传递inst
作为ArrayList<String>
,但获得Class<? extends ArrayList<String>>
并不安全。
为什么Class<? extends ArrayList<String>>
不安全?好吧,例如,您可以使用其.cast()
方法将任何内容强制转换为ArrayList<String>
,而不会发出任何警告。但是像.cast()
这样的运行时类型检查只能检查已确定的类型(Class
对象本身只代表一个具体类型);他们无法检查类型参数(在运行时不存在)。因此,对于.cast()
,您实际上会在没有警告的情况下进行未经检查的演员表,这很糟糕。