如果我将一个类声明为一个字段:
Class fooClass;
Eclipse给了我警告:
Class是原始类型。参考 泛型类应该是 参数化
这在实践中意味着什么?我为什么要这么做呢?如果我要求Eclipse进行“快速修复”,它会给我:
Class<?> fooClass;
似乎没有增加太多价值,但不再发出警告。
编辑:为什么Class是通用的?您能否举一个参数化的例子,即是否可以有效使用<?>
以外的其他内容?
Class<MyString> myStringClass = MyString.class;
而不是
Class myStringClass = MyString.class;
(但是从第一天起就使用了Java,我没注意到Class何时成为泛型);
注意:我接受了@oxbow_lakes,因为这对我来说很有意义,但它显然是一个非常复杂的领域。我会敦促所有程序员使用特定的Class<MyString>
而不是Class
。 Class<?>
比Class
更安全。
答案 0 :(得分:44)
之前的答案都没有真正解决为什么你应该更喜欢Class<?>
而不是Class
,因为从表面上看,前者似乎没有提供比后者更多的信息。
原因是,原始类型,即Class
,阻止编译器进行泛型类型检查。也就是说,如果您使用原始类型,您颠覆类型系统。例如:
public void foo(Class<String> c) { System.out.println(c); }
可以这样调用(它将编译和运行):
Class r = Integer.class
foo(r); //THIS IS OK (BUT SHOULDN'T BE)
但不是:
Class<?> w = Integer.class
foo(w); //WILL NOT COMPILE (RIGHTLY SO!)
通过始终使用非原始表单,即使您必须使用?
,因为您无法知道类型参数是什么(或受限制),您允许编译器推断程序的正确性比使用原始类型更全面。
原始类型的使用仅允许作为遗留代码兼容性的特许
你应该总是避免它们。无界通配符?
可能在别处最好描述,但实质上意味着“这是在某种类型上参数化,但我不知道(或关心)它是什么”。这与原始类型不同,后者是令人厌恶的,并且在其他语言中不存在,如Scala。
嗯,这是一个用例。假设我有一些服务接口:
public interface FooService
我想注入它的实现,使用系统属性来定义要使用的类。
Class<?> c = Class.forName(System.getProperty("foo.service"));
此时我不知道我的班级是正确的类型:
//next line throws ClassCastException if c is not of a compatible type
Class<? extends FooService> f = c.asSubclass(FooService.class);
现在我可以实例化一个FooService
:
FooService s = f.newInstance(); //no cast
答案 1 :(得分:3)
你是对的。但这可能会增加价值:似乎没有增加太多价值 但不再发出警告。
Class<FooClass> fooClass;
或者,如果更合适:
Class<? extends FooClass> fooClass;
或
Class<FooInterface> fooClass;
与泛型一般情况一样,您可以通过指定您希望变量保持的类的类型来提高类型安全性。针对原始类型的警告只是为了捕获未使用此潜力的代码片段。通过声明Class<?>
你基本上说“这是意味着来举行任何类型的课程”。
答案 2 :(得分:2)
因为,自JDK 5起,Class
现在已经具有参数化类型,这使得Class
成为通用对象。这是必要的(自Generics引入以来)编译器进行类型检查(当然是在编译时)。
Class<?>
表示“{1}}为泛型wildcard的”未知类别“。这意味着?
类型fooClass
接受类型与任何内容匹配的Class<?>
。
典型示例:
Class
您可以有效地提供更具体的参数化类型,例如:
Class<?> classUnknown = null;
classUnknown = ArrayList.class; //This compiles.
PS 请记住,Class<ArrayList> = ArrayList.class;
将无法编译(即使ArrayList属于List),但(正如Mark Peters在评论中提到的那样)Class<List> listClass = ArrayList.class;
确实编译(感谢通配符)。
答案 3 :(得分:1)
因为使用原始类型而不是参数化类型存在许多缺陷。
其中之一是如果使用原始类型,则类中的所有泛型都将丢失。甚至那些按方法定义的那些。
答案 4 :(得分:1)
Class类的Javadoc确实给出了为什么这个类存在类型参数的想法:
T - 由类建模的类型 这个
Class
对象。例如,String.class
的类型为Class<String>.
如果班级正在使用Class<?>
建模是未知的。
使用此类型参数并不是那么明显,但粗略地查看类的源代码,表明有时需要类型参数的原因。考虑newInstance
方法的实现:
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
}
return newInstance0();
}
如果没有注意到,此方法返回的对象类型是type参数的类型。这在使用大量反射的代码中非常有用,并且需要特别注意确保正确实例化正确类型的对象。
考虑问题中的示例,如果类实例被声明为:
Class<String> fooClass;
Class<Integer> barClass;
String aString;
然后,几乎不可能有以下代码进行编译:
aString = barClass.newInstance();
简而言之,如果您要使用类层次结构,并且希望执行严格的编译时检查以确保您的代码不需要执行大量instanceof
检查,那么您就是最好指定您希望使用的类的类型。指定?
允许所有类型,但有些情况下您需要更具体。