Generics, FAIL

Why isn’t this working, and how do I add an element to the collection?

import java.util.ArrayList;
import java.util.Collection;


public class CollectionTest {

    
    public void main(String[] args) {
	Collection<? extends A> list = new ArrayList<B>();
	B b = new B();
	list.add(b);  // FAIL
    }
    
    public class A {
	
    }
    
    public class B extends A {
	
    }
    
}
 

[quote]The method add(capture#1-of ? extends CollectionTest.A) in the type Collection<capture#1-of ? extends CollectionTest.A> is not applicable for the arguments (CollectionTest.B)
[/quote]
And why can I assign it ArrayList but not actually put in any Bs ::slight_smile:

I assume its a type-erasure problem, but I dont know what the fix IS.

eclipse compiler or javac? Both interpret the generics spec differently ::slight_smile:

When you declare something as a Collection<? extends A>, you can’t add objects into it of type B, since it might be designed to contain objects unrelated to B.

To see this, imagine we had a third class, class C extends A, and we had written:


Collection<? extends A> list = new ArrayList<B>();
list.add(new C());

If adds were allowed the way you’re attempting to do it, that call would succeed, and you’d have managed to insert a C object into an ArrayList of B’s, which makes for sad pandas all around.

I don’t know exactly why you need the wildcard at all without seeing the real world context, so I can’t say if this will fit your needs, but something close to your original code that does work (though you’d never use this as is, since it’s trivial) is:


Collection<? super B> list = new ArrayList<B>();
list.add(new B());

If you use the “? super B” marker you can add a B or any subclass of B to a list, since you know that the list will always be of the proper type to accept it; however, you can no longer extract elements from the list as objects of type B, because the list might be of a superclass of B, and could contain (for instance) a C object or a plain old A.

In other words, “extends” lets you read, and “super” lets you write. If you need both, you probably just want a plain old Collection.

http://echelog.matzon.dk/logs/browse/java/1228863600
from ~ [11:03:03]

example of why it should fail:

import java.util.ArrayList;
import java.util.Collection;


public class CollectionTest {

    
    public void main(String[] args) {
	Collection<? extends A> list = blah();
	list.add(new C()); // fail, correctly
    }
    
    public Collection<? extends A> blah() {
	Collection<B> list = new ArrayList<B>();
	list.add(new B());
	return list;	
    }
    
    public class A { }
    public class B extends A { }
    public class C extends A { }
    
}

Good example :slight_smile:

I avoid generic but for the very simplest of things these days!

Cas :slight_smile:

A good rule of thumb re: wildcards: if they’re showing up in actual external code, as opposed to just inside method signatures, that’s an API smell. It’s rare that you would ever mean to write an API that returns anything involving a <?>, as it’s somewhat hostile to the user of the API (it usually means that they’re getting a crippled object whose type they don’t firmly know which they either can’t read, write, or both, and this is seldom proper in an API; furthermore, it’s mean to the user, since this is one of the more glitchy and obscure “features” of Java (and provides some of the least helpful error messages - they tend to be almost C+±esque in their vagueness), and most people should never need to understand it).