题
如果我将课程声明为一个领域:
Class fooClass;
Eclipse给我警告:
课程是一种原始类型。对通用类型类的引用应参数化
这在实践中意味着什么?我为什么要敦促这样做?如果我要求Eclipse进行“快速修复”,它给了我:
Class<?> fooClass;
这似乎并没有增加太多价值,但不再发出警告。
编辑:为什么类通用?您能举一个参数化的示例吗? <?>
?
编辑:哇!我没有意识到这一点的深度。我还看过爪哇拼图器,这肯定让我害怕熊陷阱。所以我将永远使用
Class<MyString> myStringClass = MyString.class;
而不是
Class myStringClass = MyString.class;
(但是从第一天开始使用Java后,我并没有真正注意到课堂何时成为通用);
注意:我已经接受了@Oxbow_lakes,因为这对我来说很有意义,但这显然是一个非常复杂的领域。我敦促所有程序员使用特定的 Class<MyString>
而不是 Class
. 。和 Class<?>
比 Class
.
解决方案
原始类型和无限的通配符
以前的答案都没有真正解决为什么您应该喜欢的 Class<?>
超过 Class
, ,就像表面上一样,前者似乎没有比后者更多的信息。
原因是, 原类型, , IE 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!)
通过始终使用非拉瓦表格,即使您必须使用 ?
因为您不知道类型参数是什么(或由),所以您允许编译器比使用原始类型更充分地理解程序的正确性。
为什么根本有原始类型?
这 Java语言规范 说:
只允许使用原始类型作为特许权以兼容旧版代码
您应该始终避开它们。无限的通配符 ?
可能是其他地方最好的描述,但本质上是指 “这是在某种类型上进行参数化的,但我不知道(或关心)是什么”. 。这与 原始类型, ,这是一种可憎的,不存在其他具有仿制药的语言,例如 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
其他提示
这似乎并没有增加太多价值,但不再发出警告。
你是对的。但这可能会增加价值:
Class<FooClass> fooClass;
或者,如果更合适:
Class<? extends FooClass> fooClass;
或者
Class<FooInterface> fooClass;
与仿制药一样,您可以通过指定来提高类型安全性 哪一种 您希望将变量保存的课程。对原始类型的警告只是为了捕获未使用该潜力的代码。通过声明 Class<?>
您基本上是在说“这是 意思是 举行任何类型的课程”。
因为,自JDK 5以来 Class
现在已经具有参数化类型,这使得 Class
通用对象。这是必要的(因为引入了仿制药),以使编译器进行类型检查(当然是在编译时)。
Class<?>
意思是“未知类”的地方 ?
是仿制药 通配符. 。这意味着一个 fooClass
类型 Class<?>
接受 Class
谁的类型匹配任何东西。
典型示例:
Class<?> classUnknown = null;
classUnknown = ArrayList.class; //This compiles.
您可以有效地提供一种更具体的参数化类型,例如:
Class<ArrayList> = ArrayList.class;
PS 请记住,那 Class<List> listClass = ArrayList.class;
不会编译(即使ArrayList属于列表),但是(如评论中提到的马克·彼得斯) Class<? extends List> listClass = ArrayList.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();
}
如果没有注意到,则该方法返回的对象的类型是类型参数的类型。这在利用大量反思的代码中很有用,并且希望格外小心以确保正在实例化正确的对象。
考虑该问题中的示例,是否将类实例声明为:
Class<String> fooClass;
Class<Integer> barClass;
String aString;
然后,几乎不可能拥有以下代码来编译:
aString = barClass.newInstance();
简而言之,如果您要使用类层次结构,并且希望实施严格的编译时间检查,以确保您的代码不需要执行很多 instanceof
检查,然后最好指定要使用的类的类型。指定 ?
允许所有类型,但是在某些情况下,您需要更具体。
因为使用原始类型而不是参数化类型具有许多陷阱。
其中之一是,如果使用了原始类型,则丢失了类上的所有仿制药。甚至那些定义的人均。