【问题标题】:Class<List> or Class<List<?>> [closed]Class<List> 或 Class<List<?>> [关闭]
【发布时间】:2011-09-29 04:43:03
【问题描述】:

如果我们有一个泛型类的变量,比如List,它应该是什么类型?

List<Class> c1;

List<Class<?>> c2;  

【问题讨论】:

  • 我没有意见,只有 Java 语言规范 对此有意见的事实:它强烈反对使用原始类型。
  • 这个问题没有建设性,因为它被问到的方式。 (这也出现在@irreputable 的 cmets 中。他更感兴趣的是争论 Java 的设计是错误的,而不是对问题的字面回答...... IMO。)事实上,你已经成功地以建设性的方式回答了它是归功于您。
  • @irreputable - 您问题的真正动机是对 JLS 中的这些(声称的)矛盾“争论”。这不是建设性的。这是我投票结束的主要原因……不是因为我不理解这个问题,或者不知道如何回答。
  • @irreputable - 如果你真的想要人们对 JLS 是否存在矛盾的意见,那么你应该写一个问题,清楚地说明你的情况,然后要求人们提供他们的回答。但更好的方法是创建一个博客,您可以在其中阐述您的理论。
  • 仅仅因为提问者在争论并且认为他们的问题比实际更主观,并不意味着这是一个完全无用的、没有建设性的问题。如果这个问题能引起像美利通一样深入的回答,这不是一个毫无价值的问题。

标签: java oop generics


【解决方案1】:

第二个,因为第一个使用原始类型而不是泛型。

List 是原始的,但 List&lt;?&gt; 是通用的,您不应该在原始和通用之间混用和匹配。

【讨论】:

  • @irreputable:但是完全省略类型参数会将其变成原始类型。并且JLS section 4.8 说“仅允许使用原始类型作为对遗留代码兼容性的让步。强烈反对在将泛型引入 Java 编程语言之后编写的代码中使用原始类型。它是Java 编程语言的未来版本可能会禁止使用原始类型。"
  • @irreputable:不,它没有。 javadoc 表示它返回 Class&lt;?&gt;
  • @irreputable:你是如何确定的?听起来您只是不同意规范并来到这里寻找论据。好吧,你错了,但我不会再喂巨魔了。
  • @Mehrdad 就在 Daniel 的链接中
  • @Mehrdad 读到下面两行:“实际结果类型是 Class extends |X|>”。如果返回类型确实是Class&lt;?&gt;,则不应编译:Class&lt;Object&gt; c = new Object().getClass(),因为我们不能将Class&lt;?&gt; 分配给Class&lt;Object&gt;。但它确实编译,所以返回类型是 not Class&lt;?&gt;
【解决方案2】:

您想表示运行时类还是类型? (区别在于List&lt;String&gt;List&lt;Integer&gt; 是不同的类型,但共享相同的运行时类。

类型:使用type token之类的东西。

运行时类:自

    List<String> ls = new ArrayList<String>();
    Class<? extends List> c1 = ls.getClass();
    Class<List> c2 = List.class;

编译,但是

    Class<? extends List<?>> c3 = ls.getClass();
    Class<List<?>> c4 = List.class;

没有,我会选择在类型表达式中使用原始类型。将类型参数指定为 List 确实没有任何好处,因为类无法确定它,并且使用通配符类型将需要进行奇怪的转换才能将其转换为正确的类型,例如:

    Class<?> rawClass = List.class; // kludge: do not inline this variable, or compilation will fail
    Class<List<?>> classForBadAPI = (Class<List<?>>) rawClass;

编辑:为什么它不能编译

从 cmets 中解脱出来:

为什么第二个代码没有编译?该代码非常有意义。这是JDK中的设计错误吗?还是有正当理由选择?

List.class 的类型为 Class&lt;List&gt;。由于List&lt;?&gt;List 是不同的类型,Class&lt;List&lt;?&gt;&gt;Class&lt;List&gt; 是不相关的类型,但是赋值的右手类型必须是左手类型的子类型。 getClass() 的情况类似。

我不会责怪 JDK,他们只是实现了语言规范本身制定的规则,特别是:

类文字的类型C.Class,其中C 是类、接口或数组类型的名称,是Class&lt;C&gt;

(source)

方法调用e.getClass()的类型,其中表达式e具有静态类型T,是Class&lt;? extends |T|&gt;

(source)

我们写|T| 用于擦除T 类型。

(source)

...为什么会这样定义?

编译器知道e 的完整泛型类型,但为什么e.getClass() 必须返回已擦除类型。

很难给出明确的答案,因为规范没有详细说明该定义的原因。但是,这可能是因为运行时类型可能不是静态类型的子类型,这是由于不正确地抑制未经检查的警告而导致的病态情况(c.f heap pollution)。通过指定返回类型只包含擦除,规范确保即使存在堆污染,getClass() 返回的类对象是声明的返回类型getClass() 的实例。它还提醒您,程序员将要使用反射 API 访问的运行时仅考虑擦除的类型。

【讨论】:

  • 为什么第二段代码不能编译?该代码非常有意义。这是JDK中的设计错误吗?还是有正当理由选择?
  • List.class 的类型为 Class&lt;List&gt;。由于List&lt;?&gt;List 是不同的类型,Class&lt;List&lt;?&gt;&gt;Class&lt;List&gt; 是不相关的类型,但是赋值的右手类型必须是左手类型的子类型。 getClass() 的情况类似。
  • 为什么编译器认为ls.getClass()的类型是Class&lt; extends List&gt;而不是Class&lt; extends List&lt;String&gt;&gt;?它可以选择后者,无论如何这都是编译时技巧,不需要运行时类型信息。那么为什么编译器拒绝选择后者,这会简化客户端代码呢?
  • ...为了方便起见,我现在引用了规范中的相关规则。
  • @irreputable - “我确实质疑规范”。是的,那太糟糕了。因为 Java 语言 以这种方式指定,而这正是 Java 编译器必须实现的。 (如果 Java 设计人员在 2000 年左右获得了“干净的石板”,他们就不会设计出那样的 Java 泛型。但事实并非如此:出于兼容性原因,他们不得不采用“类型擦除”方法。)
【解决方案3】:
Class<? extends List<?>>

由于 List 本身在这里是一个接口,这可能是一个更好的选择。

【讨论】:

  • 不,我说的是List的类,不是任何子类
  • 没有“List 的类”,因为List 是一个接口,而不是一个类(尽管我承认List.class 是一个有效的表达式并且有一个名为List.class 在 rt.jar 中)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-26
  • 2021-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多