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.
Related
This question already has answers here:
What is PECS (Producer Extends Consumer Super)?
(16 answers)
Closed 5 months ago.
I am confused by whether wildcards <? super Number> refers to a superclass like Object or a subtype like Integer. In "Java OCP 17 Developer Complete Study Guide" by Jeanne Boyarsky and Scott Selikoff, (Chapter 9 Collections and Generics)
Generics allow you to specify wildcards. <?> is an unbounded wildcard
that means any type. <? extends Object> is an upper bound that means
any type that is Object or extends it. <? extends MyInterface> means
any type that implements MyInterface.
<? super Number> is a lower bound that means any type that is Number or a super class. A compiler error results from code that attempts
to add an item in a list with an unbounded or upper-bounded wildcard
A super class as I understand, is a parent of Number.
But then, why cannot one do this?
import java.util.ArrayList;
import java.util.List;
public class Bewildered {
static class A {}
static class B extends A {}
static class C extends B {}
public static void main(String[] args) {
List<? super B> listBs = new ArrayList<>();
listBs.add(new A());
}
}
compile error:
java: incompatible types: Bewildered.A cannot be converted to capture#1 of ? super Bewildered.B
Because you're thinking about what generics means in the wrong way.
List<? super B> does not mean: "A list that contains Bs or anything that is a supertype of B".
Because that would be utterly useless. Object is a supertype of B. All things are objects. So it's a list of absolutely anything, there's absolutely nothing useful about this list.
No, what List<? super B> means: There is a type. A specific type. It's just.. this code doesn't know what it is. We do know that it is either B or some supertype of B. (So, it's B, or A, or Object, we don't know. The caller picked one of these 3 types). This list is constrained; it cannot contain anything that is not this unknown type.
The point is, you can do this:
List<B> bees = new ArrayList<B>();
List<? super B> list = bees; // legal!
list.add(new A()); // um...
B bee = bees.get(0); // oh no!
See how this goes wrong? There's only one list here - bees and list are both just variables that refer to the exacrt same list. So you if you call list.add, you're modifying the one list that both bees and list are pointing at, so you also add it to bees.
So what's the point of <? super B>?
You can call .add(new B()). Because that works for all legal choices. Whatever the caller picked - if they gave you a List<B>, it works. If it's a List<A>, works. List<Object>? Still acceptable.
No other types fit the bill for <? super B>, so we've exhausted the options: They all 'work out'.
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.
How would you describe – in plain english – the difference between the contents of these lists?
I'm looking for a simple comparison that can be used as a reference.
/* 1 */ List< List< Dog>>
/* 2 */ List< List<? extends Dog>>
/* 3 */ List<? extends List< Dog>>
/* 4 */ List<? extends List<? extends Dog>>
The generic declarations with super are similar. So what about these:
/* 5 */ List<? super List<? extends Dog>>
/* 6 */ List<? extends List<? super Dog>>
Related questions & resources
Explanation of the get-put principle
Generics : List<? extends Animal> is same as List<Animal>?
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
http://docs.oracle.com/javase/tutorial/java/generics/
A List whose elements are lists of dogs
A List whose elements are lists of a type that extends Dog
A List whose elements are a single subtype of a list of dogs
A List whose elements are a single subtype of a (list of a type that extends Dog)
A List whose elements are a single supertype of a (list of a type that extends Dog)
A List whose elements are a single subtype of a (list of a type that super Dog)
Where "subtype", "supertype", "super", and "extends" are the "generic" versions (i.e. also includes the bounding type)
Examples with Number and subtypes, because why not. Replace Dog with Number.
List<List<Number>> might look like a 2D array of Number elements. Pretty simple.
List<List<? extends Number>> might look like a 2D array, where each row is a different subtype of number. For example, the first row might be a List<Integer>, the second row might be a List<Double>, etc.
List<? extends List<Number>> might be a List<ArrayList<Number>>, List<List<Number>>, List<LinkedList<Number>>, etc. The Number has to stay, because generics are invariant, but you can have List or any of its subtypes as the "overall" type of the elements. You can only pick one of List or its subtypes though, and the one you pick you have to stay with.
List<? extends List<? extends Number>> is similar to List<? extends List<Number>>, except now you can pick Number or any of its subclasses as the elements of the "overall" 2D array. So you can have List<List<Integer>>, List<ArrayList<Integer>>, List<LinkedList<Double>>, etc. As before, you can only pick one of List's subtypes and one of Number's subtypes.
(tricky!) List<? super List<? extends Number>> appears to be equivalent to List<List<? extends Number>>, List<Collection<? extends Number>>, etc. but not List<List<Number>> or anything concrete where a subtype of Number is used. I think this is because List<Number> isn't considered a supertype of List<? extends Number>, which I suppose makes sense due to generics being invariant. List<Object> as well as raw types (List<List>, List<Collection>, etc.) also works.
Same thing as 4, except you get either List<Number> or List<Object> (and apparently List<Serializable>) as the inner list.
As #MadProgrammer said, due to PECS (Producer-Extends-Consumer-Super), any time you have a ? extends for your generic type you won't be able to update the list, only retrieve items from it. So no add()-ing and no set()-ing.
The first one is concrete: it's a List of Lists of Dog, and nothing else.
The second one is abstract on the innermost declaration: it's a List of Lists of some class that inherits from Dog -- say Poodle. It might be a List<ArrayList<Poodle>>.
The third one is abstract on the inner list declaration: its a List of some sort of container that inherits from List -- maybe ArrayList -- of Dog. Then you might have List<ArrayList<Dog>>.
The fourth one is abstract on both: its a List of some kind of container derived from List, filled with some animal that inherits from Dog -- maybe List<ArrayList<Poodle>>.
Replace each "<" by "of".
Whenever there is a "? extends X", insert "or one of its subclasses" after X.
Consider the following codes:
class Super {}
class Sub extends Super {}
class Test {
public static void main(String[] args) {
List<? extends Super> list = new ArrayList<Sub>(); //1
list.add(new Sub()); //2
}
}
Line 1 compiles successfully, but line 2 fails compilation:
The method add(capture#2-of ? extends Super) in the type List<capture#2-of ? extends Super> is not applicable for the arguments (Sub)
My questions are:
1) Why does line 1 successfully compile?
2) Is line 1 a good practice in declaring a List (or other collections)?
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1?
4) Eclipse's autocompletion says only "null" elements are allowed in list now. Why?
Thanks a lot!
1) Why does line 1 successfully compile?
The first line compiles because List<Sub> is subclass of List<? extends Super> and that can only be true if List does not allow you to add any new members to it.
? means that you don't exactly know that it is a List<Sub> or List<Sub1>, so it is unsafe to allow adding of new elements into the list and so it does not allow it.
2) Is line 1 a good practice in declaring a List (or other
collections)?
If you already know that it is going to be List<Sub> then I don't find any use, but wildcards are used much when you are passing the List around to other classes like Utilities.
3) Why does line 2 fail compilation, after list was declared to be
type Sub in line 1?
Because as I already explained it is unsafe to add any element to list when you dont know the exact type.
4) Eclipse's autocompletion says only "null" elements are allowed in
list now. Why?
Because null is of every reference type, this is why you can assign any object a value of null.
Always remember PECS (Producer Extends Consumer Super) rule by Josh Bloch when working with generics
Good References:
Difference between ? extends and ? super
When do we require <? extends T> as compared to <T>
Capturing declarations as in line 1 are good in method arguments. See Collection.addAll(Collection<? extends E>). If you need a list with something extends Super, just use List<Super>.
You have declared your list as, it can be assigned to any sub type of Super class. So, assigning a Sub type list is OK, Compiler allows to compile. But that doesn't mean, you can add a specific Object type into it.
<? extends Super> don't means, you can add any sub type of Super. It means, You can assign any sub type collection to it.
1) Why does line 1 successfully compile?
You declare list to be "a list of something that derives from Super". You assign a list of Sub to it. Sub is "something that derives from Super".
2) Is line 1 a good practice in declaring a List (or other collections)?
No. Wildcards are for function parameters. Local variables should be as specific as possible in their generics arguments to avoid problems such as the one you're facing.
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1?
Fallacy. list was declared to have elements of "something that derives from Super", not Sub as you claim. And you can't add a Sub to the list because the "something" might be something other than Sub; it might be Sub2, and adding would be equivalent to this illegal assignment:
class Super {}
class Sub extends Super {}
class Sub2 extends Super {}
Sub2 s = new Sub();
The key misunderstanding here seems to be that you think the wildcard somehow gets substituted on assignment. It isn't. It remains a wildcard, and only compatibility checks are done.
4) Eclipse's autocompletion says only "null" elements are allowed in list now. Why?
null is the only value that is of any possible reference type, and is therefore compatible with the list no matter what the wildcard stands for.
1) Why does line 1 successfully compile?
You basically define the list to contain elements of any type that extends (or is) Super, i.e. the compiler knows that every element in that list should at least have the properties of Super.
Since Sub is a subclass of Super and thus any list containing only Sub elements also meets the requirement of all elements being instances of Super, List<? extends Super> list = new ArrayList<Sub>(); is correct.
2) Is line 1 a good practice in declaring a List (or other collections)?
As a local variable that depends on personal style, IMHO. When declaring parameters that way (or instance/static variables) that's often not only good style but also needed.
Consider a method that iterates over a collection of numbers and returns a sum.
You could declare the parameter to be Collection<Number> but then you couldn't pass a Collection<Integer> without a nasty cast. If the parameter is declared as Collection<? extends Number> you can pass Collection<Integer>.
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1?
The reason is that the compiler doesn't know the exact type of the elements in the list. Is it a list of Super or a list of Sub?
As an example take List<? extends Number> list. You don't know whether you have a List<Number>, a List<Double> or a List<Integer> and thus can't tell whether list.add( new Integer(1) ); would be ok or not. That's how the compiler sees it.
4) Eclipse's autocompletion says only "null" elements are allowed in list now. Why?
I'd have to guess here but adding null to a list would be ok since no matter what type the actual list declares, you can always cast null to that type.
The code below makes complete sense to me - its about adding an element of some type which is supertype of type T and type S is definitely such a super type , so why the compiler refuses to add 'element' into the collection ?
class GenericType<S,T extends S>{
void add1(Collection<? super T> col ,S element ){
col.add(element); // error
// The method add(capture#9-of ? super T) in the type
// Collection<capture#9-of ? super T> is not applicable for the arguments (S)
}
}
Thake an example, if A <- B <- C where <- means that is the supertype, then if S = B and T = C you cannot add an instance of S to a collection of T.
A supertype of T may be the supertype or a subtype of another supertype of T (in this case S).
Collection<? super T> does not mean "a collection that can contain T and any superclass of it" - it's actually not possible to formulate that restriction. What it means is "a collection that can only contain instances of some specific class which is a superclass of T" - basically it ensures that you can add a T to the collection.
The method can be called with a Collection<T>, yet you want to add an S to it.
new GenericType<Object,Integer>().add1(new ArrayList<Integer>(), "");
You are trying to put an element of type S into a collection of type T. Generics aren't polymorphic.
You have to take notice of 2 problems here. you are trying to create an Collection of type concreteObject extends Object and are adding an object
So when you have
Car extends Vehicle{}
ElectricCar extends Car{}
you are trying to do
Collection<? extends Car> collection;
collection.add(new Vehicle());
The second problem lies with the non-polymorphism nature of Generics. See this great explanation -> Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?