将.getClass()强制转换为类型参数

时间:2016-12-19 13:01:17

标签: java generics

我有一个通用的方法,我需要获取参数的类:

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

2 个答案:

答案 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(),您实际上会在没有警告的情况下进行未经检查的演员表,这很糟糕。