I have a class that returns an ArrayList of ArrayLists. I would like to return a Collection of Collections to abstract the output datatype.
With a standard ArrayList, its easy - just define the return type as Collection. Unfortunately, with a 2D ArrayList, that doesn't work:
//This does not compile
public Collection<Collection<Object> myFunction() {
return new ArrayList<ArrayList<Object>>();
}
//This does compile (but isn't what I want)
public Collection<ArrayList<Object> myFunction() {
return new ArrayList<ArrayList<Object>>();
}
Obviously one can create a Collection, then add the ArrayLists individually, converting them to Collections, but this is more complicated than I would expect. What is the proper way to abstract a multidimensional return type?
That's not how it works. ArrayLists are a collection. That's what
class Dog extends Animalmeans: All dogs already are animals. There is no 'converting'. You don't convert a dog to an animal, it's already one.Hence, add the ArrayLists individually, converting them to Collections is nonsense.
no, this does not compile, because that is a nonsensical statement. The thing is, collections have an
addmethod. You can add things to them. And java is a reference-based system, so in the code above there is only a single list-of-lists (count thenew- I only count one, so there must be only one list here, there cannot possibly be 2), just.. you have 2 variables pointing at the same thing. Like an address book with 2 pages, both with the same address on it.Via variable
bI could do:b.add(new HashSet<Object>()). That should be obvious: All HashSets are collections, the.add()method of variablebrequires aCollection<Object>, and..new HashSet<Object>()is aCollection<Object>, therefore, that is allowed. And indeed that is valid java here.Except.. it's the same list.
aalso points at this list. Which.. now has a HashSet in it. A variable of typeArrayList<ArrayList<Object>>has.. a HashSet in it.Oh dear.
That's all wrong.
Which is why java does not allow
Collection<Collection<Object>> b = a;here - because that is nonsense.You can opt into covariance by writing:
This is not too useful - the point of
?here is to stop you from calling 'add', and then all is well. This assignment works, and indeedb.add(new HashSet<>())no longer works now, but,b.get(0).add(someObject)also no longer works.The correct move is to go to where-ever you make that initial arraylist and make it correct right from the get-go: Make that a
new ArrayList<Collection<Object>>. Because all ArrayLists are collections, you can call.add(new ArrayList<Object>())on a variable of typeArrayList<Collection<Object>>, no problem. And you can assign anArrayList<Collection<Object>>to a variable of typeCollection<Collection<Object>>.Generics are invariant - if you need type FourFootedAnimal, only that type will do. You can't provide a subtype or a supertype. But outside of generics, java is covariant - FourFootedAnimals will do, but so will a Dog or a Cat. If you want covariance, use
? extends(and?on its own is short for? extends Object), but note that this disables all attempts to add.