Struggling with understanding <? extends T> wildcard in Java - java

I have a very basic question.
The code below doesn't compile (assume Apple Extends Fruit):
List<? extends Fruit> numbers = new ArrayList<>();
numbers.add(new Apple()); //compile time error
When reading about why not, I understand the words but not the concept :).
Let's assume first Fruit is NOT an abstract class. I understand that that since we're dealing with multiple subtypes all of which extend Fruit. Supposedly since we can't tell the exact type of fruit, we can't put anything in the collection. There's a couple things I don't understand:
1) Apparently we cannot know which fruit it is which confused me. Wouldn't we be able to tell the specific type through a typeof or other instanceof check while iterating through the collection?
2) Assuming Fruit is a concrete class, why wouldn't we be allowed to add instances of Fruit? It seems like that would make sense because you would know at minimum the API for Fruit. Even if you don't know the exact subtype of Fruit, at least you can invoke the standard methods on Fruit().
I feel like this should be rather obvious but something isn't clicking for me. Any help is appreciate. Thanks!

The best way to understand this is to think of the wildcard as saying something about the list, not the fruit. In other words:
List<Banana> allBananas = getMyBananas();
enumerateMyFruit(allBananas);
static void enumerateMyFruit(List<? extends Fruit> myFruit) {
for (Fruit fruit : myFruit)
System.out.println(fruit);
}
When we pass allBananas to enumerateMyFruit, inside the method we lose information about the original declared type of the list. In this example we can very clearly see why we shouldn't be able to e.g. put apples in a List<? extends Fruit>, because we know that the list is actually a List<Banana>. Again, the wildcard is telling us something about the declared type of the list.
List<? extends Fruit> should be read as something like "a list originally declared to hold Fruit or some subtype of Fruit, but we don't know what that declared type is anymore". All that we know is that everything we pull out of the list is a Fruit.
Also, you are right, we could iterate the list and use instanceof to find out what is really in the list, but this wouldn't tell us the original declared type of the list. In the above code snippet we would find out that everything in the list turned out to be a Banana, but I could have just as easily declared allBananas as a List<Fruit>.
You might also see why a List<Dog> is not a List<Animal> which explains some of this. The wildcard is how we have covariance among generic types. a List<Dog> is not a List<Animal> but it is a List<? extends Animal>. This comes with the restriction that we can't add to a List<? extends Animal>, because it might be a List<Dog>, a List<Cat> or something else. We don't know anymore.
There's also the ? super, which is the opposite. We can store Fruit in a List<? super Fruit> but we don't know what kinds of objects we will pull out of it. Its original declared type might actually be e.g. a List<Object>, with all kinds of other stuff in it.

First remember that for generic parameters without wildcards, you can't substitute one for another. If a method takes a List<Fruit> it won't take a List<Apple>, it has to be an exact match. Also remember this is about the static type of the variable, there's no direct connection to the contents. Even if your List<Fruit> contains all Apples, you still can't substitute it for a List<Apple>.
So we're talking about type declarations, not about what's in the collections.
Also remember instanceof is done at runtime, generics work at compile time. Generics are about helping the compiler figure out what types things are so you don't have to resort to instanceof and casting.
When a method foo takes a parameter with the generic type List<? extends Fruit>, that is a way of saying that the method can take a range of types, in this situation those being any of the following:
You can pass in a List<Fruit>
You can pass in a List<Banana>
You can pass in a List<Apple>
(etc., for whatever subtypes of Fruit you have)
So your method can work with a list of any of these, however, the method body has to be valid for any of them. When you add an Apple to the list, that works for the case where what's passed in is a List<Apple>, it works for List<Fruit>, for List<Banana> not so much. (And making Fruit concrete doesn't help matters,
adding a Fruit doesn't work for the List<Apple> case either.)
That is why there's a rule that anytime the wildcard type extends something, adding stuff is not possible, there's no way it can work for all the possible types that can be passed in.

Related

Practical usage of wildcard generics in Java

I recently had an exam on Java, and there was a wide section about wildcard generics in Java. However, there is very little said about their usage in practice. When should we use them? Let's see a typical usage:
void check(Collection<? extends Animal> list) {
// Do something
}
What the documentation says, that this collection does not allow to add any elements to the list. So basically wildcards can be used for making collections read-only. Is that their only usage? Is there any practical need for that? For the last four years I took part in a lot of programming projects in Java, but I haven't seen any project that would use extensively such a feature as wildcard.
So, from the practical point of view, are there any situations when wildcard generics are unavoidable and necessary?
So, from the practical point of view, are there any situations when
wildcard generics are unavoidable and necessary?
I don't think they are 'unavoidable and necessary' because the Java compiler erases them anyway. However, when using them you get the benefit of a tighter type check during compile-time and you avoid type casting. Who wants to type cast anyway? :)
Guidelines for Wildcard Use
Type Erasure
void check(Collection<? extends Animal> list) {
list.add(new Animal()); // compile time error
list.add(new Dog()); // compile time error. Dog is subclass of Animal class.
}
Java has develop such generics because to disallow the programmar to code whatever they want otherwise if it is allowed then later they will find a mess in run-time environment.
Sometime in programming you will get a scenario where your method check would not wan't to add element in the list but want to read those element.
You can only add null values.
In brief, what you are saying is wrong.
It has nothing to do to "make collections read-only".
We can still add elements to the input list, because Collection did declare a add(E) method.
The wildcard is straight-forward I think: You actually want to constraint the input type, because your logic is only reasonable for certain type.
For your example, your check maybe using some method of Animal
void check(Collection<? extends Animal> list) {
// Do something
for (Animal a : list) {
a.checkAge(); // checkAge() is method of Animal
}
}
Without ? extends Animal, the above code will not work, as the incoming list can be collection of anything (not Animal).
Therefore, we want to make sure the incoming collection to Collection or Collection etc, so that our code actually make sense as we are retrieving elements from the list and treated the element as Animal.

Why is this such use of generic and wildcard not allowed?

This code
static void writeTo(List<? super Apple> apples) {
apples.add(new Apple());
apples.add(new Jonathan());
}
The author of this code stated that
The argument apples is a List of some type that is the base type of Apple; thus you know that it is safe to add an Apple or a subtype of Apple. Since the lower bound is Apple,
Jonathan is a subclass of Apple.
But when I tried this
List<Jonathan> loj = new ArrayList<Jonathan>();
listSuper(loj);
It gave me this error
The method listSuper(List<? super Apple>) in the type Basket<T> is not applicable for the arguments (List<Jonathan>)
Where listSuper looks like this
static void listSuper (List<? super Apple> cont) {}
How does the two differ?
Also what confuses me on the first code that I posted is that
I thought ? super T means that any base type of T. but from the looks of it he added a subtype of T. I am confused.
List<? super Apple> means a List you can add an Apple to (and since Jonathan is an Apple, you can put Jonathans into a List of that type as well).
It can be List<Apple>, List<Fruit> or List<Object>, but not List<Jonathan>, since you cannot put arbitrary Apples into List<Jonathan>. As you can see, in this case ? can be an Apple or any of its superclasses.
List<? extends Apple> means a List you can get an Apple from. It can be List<Apple> or List<Jonathan>, but not List<Fruit>, since List<Fruit> is not guaranteed to contain only Apples.
This explanation is known as "producer - extends, consumer - super" rule: if parameter acts as a consumer of elements, it should be declared with super, and vice versa.
Jonathan is a subtype of Apple, not a supertype. It would match <? extends Apple> but does not match <? super Apple>
The author of the code was wrong. You can't pass a subclass of Apple to a method that takes ? super Apple, only Apple itself and superclasses of Apple. If you want to be able to add subclasses of Apple, you need to use ? extends Apple.
The type parameter must be a supertype of Apple, not a subclass, which is what Jonathan is. So, for instance, this would be valid:
List<Fruit> loj = new ArrayList<Fruit>();
listSuper(loj);
How does the two differ?
Also what confuses me on the first code that I posted is that I
thought ? super T means that any base type of T. but from the looks of
it he added a subtype of T. I am confused.
You must distinguish between (1) what you can insert into a generic list and (2) what can be sent as an argument to method with a generic list parameter.
You can insert subtypes of Apple into apples because the bound type parameter is a base class which has the ability to reference all its subtypes.
If Jonathan isn't a super type of Apple, then it's generically incorrect to try and send a list of Jonathan to that method, since I'd be allowed to insert Apples into a list of Jonathans. Then you'd have references of type Jonathan accessing the properties and methods of objects it knows nothing about, which isn't type-safe.

Class<? extends SomeSuperclass> is apparently not allowed. What makes sense instead?

This is an Android app, but presumably it happens the same in Java. I have a type LevelFactory from which I derive Level1Factory, Level2Factory, etc. I want to have an array of these classes so I can instantiate a level given the array index.
I can just have a Class[] and put them in that and then just cast them to LevelFactory when I need to use them, but I was wondering what the proper thing to do is.
This is obviously an error "Incompatible types":
new Class<LevelFactory>[] {Level1Factory.class,Level2Factory.class};
However, I was surprised to see that this is also an error "Generic array creation":
new Class<? extends LevelFactory>[] {Level1Factory.class,Level2Factory.class};
The following works, but it gives the "Unchecked assignment" warning when assigned to a variable:
new Class[] {Level1Factory.class,Level2Factory.class};
The last is the only option I can get to work. I just ignore the warning, but I would like to do it using generics if that's actually possible.
I would recommend you to read Item 25 "Prefer lists to arrays" of book "Effective Java". There Joshua Bloch writes:
Why is it illegal to create a generic array? Because it isn’t typesafe. If it were
legal, casts generated by the compiler in an otherwise correct program could fail at
runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system.
UPD: Maybe with concrete example it would be more understandable.
First of all arrays are covariant which means that SuperClass[] can be cast to SubClass[] and vice versa. It also means that it's legal to cast AnyConcreteClass[] to, say, Object[].
So Let's assume that it's possible to have Set<Cat>[] (but it is NOT). If somebody cast this array to Object[] and then add there a set of Dog instances, Java couldn't guarantee anymore that our array contains only sets of Cat instances. Breaking type safety it breaks essence of generics. That is why it's illegal have generic array.
Set<Cat>[] cats = new Set<Cat>[]; // illegal
Object[] objects = cats;
objects[1] = new Set<Dog>();
cats[1].add(new Cat()); // Oops! TypeCastException
Honestly saying this example also was taken from Effective Java :)
Two questions:
Do you really need an Array? Arrays don't work great with generics. So an ArrayList<LevelFactory> might be the better solution
Do you really need the downcast to the special type (Level1Factory, Level2Factory)? If they have a common super method which is defined in LevelFactory (lets say Level getLevel()) you should not need to downcast them. Just call getLevel() and you get the correct Level instance from your factory.
Another note because this seems to be a common pitfall:
new Class<? extends LevelFactory>
This is not a valid statement (it does not matter if its an array or not). <? extends T> is only valid for the type on the left side. It defines that the generic type of the created Object can be T or derived from T. For Collections this does not mean that they can store objects of T or derived from T (which can a Collections of T anyway).
List<LevelFactory> list = new ArrayList<LevelFactory>() This means you can add objects of LevelFactory, Level1Factory and Level2Factory to list. When you want to receive objects from list they are of type LevelFactory.
List<? extends LevelFactory> list = new ArrayList<LevelFactory>() Means you can receive objects of LevelFactory from list. BUT you cannot add any object to list in a typesafe way because you don't know the exact generic type of list. That because you can also assign new ArrayList<Level1Factory>() to list. Which means that you can't even add LevelFactory objects to list because they don't implement Level1Factory.
In general <? extends Something> on collections is not what you want in most cases.
You can't create array this way new SomeClass<Type>[10] but only this way new SomeClass[10]. Consider using ArrayList<SomeClass<Type>> instead.

What if I implement List and call add in a method that accepts List<? extends SuperType>

I have read that the following code snippet will result in a compiler error
void tryAddingToList(List<? extends SuperType> list) {
list.add(new SubType());
}
Now my question is:
Who exactly sets the restriction and how is the restriction set? Does the compiler specifically check for the add method call in such a method implementation?
If yes, what if I implement my own List and instead of add, introduce a new addToList method that does the same thing as add, and call that in the tryAddingToList method?
Even better, what if I implement my get method such that it also "secretly" adds something to the list (don't ask me why anyone would do that; I'm just curious).
You should use super instead of extends here.
void tryAddingToList(List<? super SubType> list) {
list.add(new SubType());
}
Assume there are 3 types SuperType, SubType1, SubType2. In your original code, List<? extends SuperType> could be List<SubType2>, and you can't add a SubType1 to List<SubType2>.
You should cast SubType to SuperType explicitly. Because not all objects of SuperType are SubType(may be typed other sub-classes). This may give you some hint. Good luck.
Yes, it's indeed the compiler that applies the restriction. Most of the information (not all) will be erased when actually compiled to byte code. You should interpreted generics as extra information for the developer, so we don't make silly mistakes.

Inheritance and casting for List Objects

I'm having trouble casting a List of Fruit down to the Fruit subclass contained in the List.
public class Response {
private List<Fruit> mFruitList;
public List<Fruit> getFruitList() {
return mFruitList;
}
}
public class Fruit {
}
public class Orange extends Fruit {
}
List<Fruit> oranges = response.getFruitList();
How do I cast oranges so that it is a List of class Orange? Is this a bad design pattern? Basically I am getting a JSON Response from a server that is a List of Fruit. For each specific call to the Web Service, I know what subclass of Fruit I will get and so I need to cast that List appropriately.
If you known for each specific call that what subclass of Fruit you will get then you should use generics instead of casting lists.
public class Response<T extends Fruit> {
private List<T> mFruitList;
public List<T> getFruitList() {
return mFruitList;
}
}
Response<Orange> response = // create
List<Orange> oranges = response.getFruitList();
EDIT: By templates I meant generic types. Sorry, I had too much C++ nowadays
The whole idea behind typecasts is to be able to tell the compiler, "Hey, I know more about this than you do." In your code, the compiler cannot safely downcast the List<Fruit> to List<Orange> because it can't know what the list will contain at runtime.
If you're absolutely certain that the list will be only Orange instances, and it makes your code more manageable to downcast, go for it.
List<Orange> oranges = (List<Orange>) response.getFruitList();
The compiler will give you a warning, of course, since you're doing something it thinks you shouldn't do. And just know that the JVM may have the last laugh by throwing a CastClassException if you were wrong!
Think of generics like a gate for what types of objects a list can contain. Because of this inheritance and casting won't work in the way you would expect. In the example you gave you could put both Oranges and Apples in your List<Fruit>. If the list has both apples and oranges how can you cast it to a List<Orange>.
If you need a List<Orange> then why even bother with the List<Fruit>. If you are explicitly casting it anyway and you know exactly what it contains its probably a needless abstraction.
If you are working with an API you can't change but you know exactly what it contains then you should loop through with an instanceof check just to make sure and explicity cast each Fruit instance to Orange when you need to the Orange API.
You should cast List, and test if each elements are instanceof Orange, and after the test cast in Oranges. This is the "best pratice".
Given
List<Fruit> getFruits() {...}
You can't typecast
List<Orange> oranges = (List<Orange>) getFruits();
Due to type erasure, at runtime the type of getFruits is just List. The compiler will not even let you do the downcast (I was in doubt, so I tried in Eclipse before answering).
You could tell the compiler that your list will contain some subclass of Fruit, in that case, you need to use a wildcard on you method:
List<? extends Fruit> getFruits() {...}
Then the cast becomes possible, but with a type safety warning:
#SuppressWarnings("unchecked")
List<Orange> oranges = (List<Orange>) getFruits();
Given that the runtime type of getFruits is List, you can just discard the generics type information and use an unsafe assigment:
#SuppressWarnings("unchecked")
List<Orange> oranges = (List) getFruits();
Maybe a more elegant way as it clearly states your intention, although requiring more system resources would be:
List<Orange> oranges = Arrays.asList((Orange[])getFruits().toArray())
Arrays in Java preserve their type information at runtime, so the cast is valid and "safe" from the compiler perspective, but it can throw a runtime exception if you pass some apples in the fruit basket.

Categories

Resources