如果我将课程声明为一个领域:

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.classClass<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 检查,然后最好指定要使用的类的类型。指定 ? 允许所有类型,但是在某些情况下,您需要更具体。

因为使用原始类型而不是参数化类型具有许多陷阱。

其中之一是,如果使用了原始类型,则丢失了类上的所有仿制药。甚至那些定义的人均。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top