This question already has an answer here:

Can someone explain the following behavior to me?

I have a list of X and use the addAll() method to add elements. These elements are returned by a method using generic types. Method getA() returns < T extends A > with A being a class. Method getI() returns < T extends I > with I being an interface (see code below).

Difference: with listX.addAll(getA()) I get a compile error (as expected), but listX.addAll(getI()) compiles (throws a runtime error when element is cast to X).

Simpified code:

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<>();
}

Am I missing something? Shouldn't I get a compile error both times?

That behavior seems to be new with Java 8, with versions below I have gotten compiler errors in both cases.

share|improve this question

marked as duplicate by Holger java 1 hour ago

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

2  
Where does B come in? And which compiler are you using? – shmosel 6 hours ago
1  
You're iterating over an empty list. How are you getting a runtime error? – shmosel 6 hours ago
2  
But you haven't shown the runtime error. I suspect it involved ignoring a warning. – shmosel 6 hours ago
2  
This has nothing to do with addAll. You could simplify the example to Collection<? extends X>z = getI(); I would remove "addAll" from the name of the question, and from the given example. – user889742 6 hours ago
3  
Just tested with Java 7 and it seems to show the same behavior. – shmosel 6 hours ago

I'd like to simplify the question and Shmosel's answer as follows:

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;
}

getI() can potentially return something that extends X and implements I, which is why it compiles. Normally, the type it actually returns would depend on something, for example an argument passed into the function.

getA() cannot return something that is an X, since it returns something that extends A, which does not extend X.

share|improve this answer

listX.addAll(getA()); doesn't compile because there's no possible subclass of X that's also a subclass of A.

listX.addAll(getI()); does compile because there could be a subclass of X that also implements I.

share|improve this answer
4  
A subclass of X that also implements I should be declared as <T extends X & I> not simply <T extends I>. – Nicolas Filotto 6 hours ago
1  
Makes sense to me. – user889742 6 hours ago
2  
@NicolasFilotto getI() is not declaring that T extends X. It's just promising to return a list of T, and letting the caller determine what type that is. It's the caller's responsibility to ensure its type argument is compatible with I, which in the case of X, it is. – shmosel 6 hours ago
2  
@SrikanthA I would expect it to be in the JLS, if anything. But as I mentioned, I couldn't reproduce the discrepancy between Java 7 and Java 8. – shmosel 6 hours ago
3  
@NicolasFilotto The onus here is on the getI() method to ensure it can safely return a List<T>, regardless of what T may be. A more plausible scenario would be where there was an input parameter using type T that could be used to produce a return value. OP's case is unusual in that it's not possible to return a list containing anything but null items without a compile error or warning. Since he's returning an empty list, it is perfectly safe. – shmosel 6 hours ago

Not the answer you're looking for? Browse other questions tagged or ask your own question.