问题 为什么使用原始类型变量会影响签名而不引用类型参数?


展望另一个  我碰到了1.8.0_112 Sun-Oracle编译器的这种有趣的行为(我没有和其他人一起测试过):

import java.util.List;

interface Alpha<T> {
   List<Integer> intList();
}

interface Beta {
   List<Integer> intList();
}

class Main {

   public static void main(String[] args) {

      Alpha rawAlpha = null;
      Alpha<Character> charAlpha = null;
      Alpha<?> qmAlpha = null;
      Beta beta = null;

      for (Integer i : charAlpha.intList()) {}
      for (Integer i : qmAlpha.intList()) {}
      for (Integer i : beta.intList()) {}
      for (Integer i : rawAlpha.intList()) {}
   }
}

编译器 只要 在最后一个for循环失败:

error: incompatible types: Object cannot be converted to Integer
      for (Integer i : rawAlpha.intList()) {}
                                       ^
1 error

尽管如此 intList() 返回列表类型 List<Integer> 在 Alpha 不依赖于类型参数 T,似乎是 <Integer> 在编译时被删除。

请注意,如果我们声明一个非泛型接口 Beta 从理论上讲,这相当于参考原始数据 Alpha,没有问题。

这是预期的行为吗?是否有人能指出涵盖这一点的语言规范段落?如果这不是一个错误,它至少看起来反直觉和非生产性;或许是为了背部可比性而做的?


6789
2018-03-28 18:05


起源

当泛型类型用作原始类型时,它会丢失所有它的泛型,而不仅仅取决于您没有给出的类型。 - Peter Lawrey
@PeterLawrey是的,似乎是这样,但问题是为什么? - Valentin Ruano
我问过Java 5.0发布时的一个开发人员,在我看来,没有太多资源可以解决除了以外的情况,它既可以是向后兼容的原始类型,也可以是通用的。没有一点原始,但仍然有一些理智的后备。 - Peter Lawrey
为什么?为什么,因为JLS当然这么说! - Lew Bloch
即使在11年前,人们也认识到稀薄类型可能就是答案。也许他们将使用Java 10。 - Peter Lawrey


答案:


说这个(有点不清楚)的JLS位于 JLS 4.8

未从其超类或超接口继承的原始类型C的构造函数(第8.8节),实例方法(第8.4节,第9.4节)或非静态字段(第8.3节)的类型是对应于的原始类型在与C对应的泛型声明中擦除其类型

所以,从那以后 rawAlpha 是一种原始类型,类型 rawAlpha.intList 是擦除 List<Integer> intList()。那个擦除是 List intList()

至于为什么,我没有引用方便,但原始类型只是用于向后兼容的Java。这意味着他们只需要像仿制药一样工作;您要求的是代码,它比以前更好一些。这不是不合理的,但并不是他们决定的。 :-)


12
2018-03-28 18:14



mmm ...“是对应于C中通用声明中类型擦除的原始类型”......我会说文本有点开放式解释......所以C就在这里 Alpha<T> 对?因此,文本将读作“的类型 Alpha.intList 是与擦除相对应的原始类型 List<Integer> 在对应的通用声明中 Alpha 所以 Alpha<T>...如果是声明为返回的方法 List<T> tList() 这句话完全有道理但是 List<Integer>?为什么要擦除 T 会影响 Integer 这里? - Valentin Ruano
@ValentinRuano因为擦除了 List<Integer>是 List  - 擦除手段 没有 通用信息被保留,而不仅仅是 T 被删除了。这就是你不能问的原因 foo instanceof List<Integer>。 - yshavit
我猜你是对的。对我而言,目前还不清楚这最后一部分意味着什么:“在与C相对应的通用声明中”。在我看来,如果你是对的(如果你愿意,可能是99.9%),那么句子的那一部分可以简单地省略。所以说它的擦除是不够的 List<Integer>是 List 但你需要说的是擦除 List<Integer>是 List 在对应的通用声明中 Alpha 所以 Alpha<T> 无论那意味着什么,这对我来说再次听起来并不清楚。 - Valentin Ruano
JLS语言需要一些时间来习惯,但它非常客观,根本不会“受到解释”。它非常准确地说明如果使用原始类型,其所有成员也将被视为原始类型。这就是为什么你得到了你得到的结果,@ ValentinRuano。 - Lew Bloch