使用addAll()编译器的ArrayList显示泛型的不同行为

fir*_*cat 17 java generics java-8

有人可以向我解释以下行为吗?

我有一个列表X并使用该addAll()方法添加元素.这些元素由使用泛型类型的方法返回.方法getA()返回< T extends A >A作为一类.方法getI()返回< T extends I >I作为一个接口(见下面的代码).

差异:listX.addAll(getA())我得到编译错误(如预期的那样),但是listX.addAll(getI())编译(当元素被强制转换时抛出运行时错误X).

简化代码:

interface I {}
class A implements I {}

class X {}

public void test() {   
    List<X> listX = new ArrayList<>();
    listX.addAll(getA());

    listX.addAll(getI());
    for (X x : listX) {}
}
public <T extends A> List<T> getA() {
    return new ArrayList<>();
}
public <T extends I> List<T> getI() {
    return new ArrayList<>();
}
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?我不应该两次都得到编译错误吗?

对于Java 8来说,这种行为似乎是新的,下面的版本我在这两种情况下都遇到了编译器错误.

Gon*_*n I 8

我想简化问题,Shmosel的答案如下:

interface I {}
class A implements I {}

class X {}

public void test() {   
    X temp = getI();  // compiles
    X temp2 = getA();  // does not compile
}

public <T extends I> T getI() {  
    return null;
}
public <T extends A> T getA() {  
    return null;
}
Run Code Online (Sandbox Code Playgroud)

getI()可以返回扩展X并实现I的东西,这就是它编译的原因.通常,它实际返回的类型取决于某些东西,例如传递给函数的参数.

getA()不能返回一个X的东西,因为它返回一个扩展A的东西,它不会扩展X.


shm*_*sel 7

listX.addAll(getA());不编译,因为没有可能的子类也是X它的子类A.

listX.addAll(getI());编译,因为可能有一个子类X也实现I.

  • 同时实现`I`的`X`的子类应声明为`<T extends X&I>`而不仅仅是`<T extends I>`. (4认同)
  • @NicolasFilotto这里的onus是在`getI()`方法,以确保它可以安全地返回`List <T>`,无论什么`T`可能.更合理的情况是使用类型"T"的输入参数可以用来产生返回值.OP的情况很不寻常,因为在没有编译错误或警告的情况下,无法返回包含除空项之外的任何内容的列表.由于他正在返回一个空列表,因此非常安全. (4认同)
  • @NicolasFilotto`getI()`并没有声明`T`扩展了`X`.它只是承诺返回一个`T`列表,并让调用者确定它是什么类型.调用者有责任确保其类型参数与"I"兼容,在"X"的情况下,它是. (2认同)
  • @SrikanthA我希望它在JLS中,如果有的话.但[正如我所提到的](http://stackoverflow.com/questions/41673016/arraylist-using-addall-compiler-shows-different-behavior-with-generics/41673357#comment70544429_41673016),我无法重现之间的差异Java 7和Java 8. (2认同)
  • @Nicolas Filotto:这里没有错误,因为代码是正确的.只要列表在问题代码中保持为空,它就不会在运行时抛出.很快,当您尝试使用它做一些有意义的事情时,会出现警告甚至错误. (2认同)
  • @Srikanth答:规则没有改变.更改的内容是[改进的类型推断](https://docs.oracle.com/javase/8/docs/technotes/guides/language/enhancements.html#javase8),它使编译器能够推断出类型嵌套调用用例. (2认同)