Class<T>和静态方法Class.forName()让我抓狂。

12

这段代码无法编译。我想知道我哪里出错了:

private static Importable getRightInstance(String s) throws Exception {
 Class<Importable> c = Class.forName(s);
 Importable i = c.newInstance();
 return i;
}

Importable 是一个接口,字符串 s 是一个实现类的名称。编译器会报错:

./Importer.java:33: incompatible types
found   : java.lang.Class<capture#964 of ?>
required: java.lang.Class<Importable>
  Class<Importable> c = Class.forName(format(s));

感谢任何帮助!

所有的解决方案

Class<? extends Importable> c = Class.forName(s).asSubclass(Importable.class);

并且

Class<? extends Importable> c = (Class<? extends Importable>) Class.forName(s);

Class<?> c = Class.forName(format(s));
Importable i = (Importable)c.newInstance();

出现如下错误(我不理解):

Exception in thread "main" java.lang.IncompatibleClassChangeError: class C1 
has interface Importable as super class

C1实际上正在实现Importable(因此理论上可以转换为Importable)。


这个 IncompatibleClassChangeError,是在 forName 过程中出现的吗?或者是在 newInstance 过程中出现的吗?无论如何,这完全是一个不同的问题,可能与泛型没有任何关系,你应该为此提出一个新的问题。 - polygenelubricants
是的,这是堆栈跟踪的最后一部分: 在java.lang.Class.forName(Class.java:169) 在Importer.getRightImportable(Importer.java:33) 在Importer.importAll(Importer.java:44) 在Test.main(Test.java:16) - Metz
提出一个新问题。那是完全不同的问题。这个问题已经基本解决了(多亏了doublep)。 - polygenelubricants
5个回答

35

使用运行时转换:

Class <? extends Importable>  c
    = Class.forName (s).asSubclass (Importable.class);

如果s指定的类没有实现接口,运行时将会抛出异常。


4
+1 是避免丑陋的石膏固定的好方法,我之前不知道这个。 - mdma

6

尝试:

Class<? extends Importable> klaz = Class.forName(s).asSubclass(Importable.class);

以下是一些代码片段,用于说明问题:
Class<CharSequence> klazz = String.class; // doesn't compile!
// "Type mismatch: cannot convert from Class<String> to Class<CharSequence>"

然而:
Class<? extends CharSequence> klazz = String.class; // compiles fine!

因此,对于一个interface,你肯定需要使用上限通配符。而asSubclass则是由doublep建议的。

API链接

  • <U> Class<? extends U> asSubclass(Class<U> clazz)
    • 将此Class对象转换为表示指定类对象所表示类的子类。检查强制类型转换是否有效,如果无效,则抛出ClassCastException异常。如果此方法成功,则始终返回对此类对象的引用。

相关问题

另请参阅


在我的安装上可以工作,但会抱怨未经检查的转换。我无法不想到是否有更好的方法... - crazyscot
我找到了一种稍微更干净的方法,可以消除警告,我即将发布为答案。 - crazyscot

2

这样的东西可能就能起到作用:

Class<?> c1 = Class.forName(s);
Class<? extends Importable> c = c1.asSubclass(Importable.class);
return c.newInstance();

如果你传入错误的内容,要小心ClassCastExceptionNoClassDefFound。正如@polygenelubricants所说,如果您能想出某种避免使用Class.forName的方法,那就更好了!


0
其中Importable是一个接口,字符串s是实现类的名称。
编译器无法知道这一点,因此会出现错误。
使用强制类型转换。将构造对象强制转换更容易(因为它是一个已检查的强制转换),而不是类对象本身。
Class<?> c = Class.forName(s);
Importable i = (Importable) c.newInstance();
return i;

0
问题在于Class.forName是一个具有以下定义的静态方法: public static Class forName(String className) throws ClassNotFoundException
因此,它根本不是绑定参数类型,编译器肯定会在这里抛出转换警告,因为它无法保证类的字符串名称始终会给你接口的实现。
如果您确定传递到该方法中的字符串名称将是接口的实现,则可以使用SuppressWarnings注释。但我认为没有其他更清晰的方法来使用带有泛型的forName。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接