Yet another novice, trying to comprehend Java Generics. I've observed all topics, I found, but I still have huge questions. Could you please explain me the following things:
<? extends SomeClass>
means, that ?
is "any type", and extends SomeClass
means, that this any type can be only a subclass of SomeClass
. OK, I write two elementary classes:abstract class Person { private String name; public Person(String name) { this.name = name; } } class Student extends Person { public Student(String name) { super(name); } }
Class Student
will be ?
in our example. ? extends Person
, to be precise. Then I'm trying to add new student to ArrayList, that, as I understand from written above, applies all classes, that are subclasses of Person:
Student clarissa = new Student("Clarissa Starling"); List<? extends Person> list = new ArrayList<>(); list.add(clarissa); //Does not compile
Eclipse says:
"The method add(capture#3-of ? extends Person) in the type List is not applicable for the arguments (Student)"
How can class Student
be not applicable, when we declared List, paramethrized by <? extends Person>
, and Student
exactly extends class Person
?
Nevertheless, the following code:
List<? super Person> list = new ArrayList<>(); list.add(clarissa);
compiles and works well (list.get(0)
, passed to println method, shows me the correct result of toString
invocation). As I understand, List<? super Person>
means, that I can pass to this list any type, that is super type for our Person
class (in our case it is Object
class only). But we see, that, contrary to logic, we can easy add subclass Student to our List<? super Person>
!
OK, put aside our emotions, and let's see, what can happen with Clarissa Starling in our collection. Let's take our class Student
, and add a couple of methods to it:
class Student extends Person { private int grant; public Student(String name) { super(name); } public void setGrant(int grant) { this.grant = grant; } public int getGrant() { return this.grant; } }
Then we pass an object, instantiated from this renewed class (our object "clarissa", for example), to List<? extends Person>
. Doing this, we mean, that we can store subclass in the collection of its superclasses. Maybe, I don't understand some fundamenthal ideas, but at this stage I don't see any difference between adding subclass to the collection of its superclasses and the assigning of the reference to object "clarissa" to variable, typed Person. We have the same reducing of invokable methods, when we want to treat one of them, using our superclass variable. So, why List<? extends SomeClass>
does not work the same way, wherein List<? super SomeClass>
works conversely?
<T>
(or <E>
, or any other letter from appropriate part of JLS) and <?>
. Both <T>
and <?>
are typeholders, so why we have two "keywords" (this symbols are NOT keywords, I just used this word for emphasizing the heavy meaning of both symbols in Java language) for the same purpose?The extends keyword extends a class (indicates that a class is inherited from another class). In Java, it is possible to inherit attributes and methods from one class to another. We group the "inheritance concept" into two categories: subclass (child) - the class that inherits from another class.
super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .
extends Number> represents a list of Number or its sub-types such as Integer and Double. Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object.
The super keyword in Java is a reference variable that is used to refer parent class objects. The super() in Java is a reference variable that is used to refer parent class constructors. super can be used to call parent class' variables and methods. super() can be used to call parent class' constructors only.
The way I look at it is this - the placeholder T
stands in for a definite type and in places where we need to know the actual type we need to be able to work it out. In contrast the wildcard ?
means any type and I will never need to know what that type is. You can use the extends
and super
bounds to limit that wildcard in some way but there's no way to get the actual type.
So, if I have a List<? extends MySuper>
then all I know about it is that every object in it implements the MySuper
interface, and all the objects in that list are of the same type. I don't know what that type is, only that it's some subtype of MySuper
. That means I can get objects out of that list so long as I only need to use the MySuper
interface. What I can't do is to put objects into the list because I don't know what the type is - the compiler won't allow it because even if I happen to have an object of the right type, it can't be sure at compile time. So, the collection is, in a sense a read-only collection.
The logic works the other way when you have List<? super MySuper>
. Here we're saying the collection is of a definite type which is a supertype of MySuper
. This means that you can always add a MySuper
object to it. What you can't do, because you don't know the actual type, is retrieve objects from it. So you've now got a kind of write-only collection.
Where you use a bounded wildcard versus the 'standard' generic type parameter is where the value of the differences start to become apparent. Let's say I have 3 classes Person
, Student
and Teacher
, with Person
being the base that Student
and Teacher
extend. In an API you may write a method that takes a collection of Person
and does something to every item in the collection. That's fine, but you really only care that the collection is of some type that is compatible with the Person
interface - it should work with List<Student>
and List<Teacher>
equally well. If you define the method like this
public void myMethod(List<Person> people) { for (Person p: people) { p.doThing(); } }
then it can't take List<Student>
or List<Teacher>
. So, instead, you would define it to take List<? extends Person>
...
public void myMethod(List<? extends Person> people){ for (Person p: people) { p.doThing(); } }
You can do that because myMethod
never needs to add to the list. And now you find that List<Student>
and List<Teacher>
can both be passed into the method.
Now, let's say that you've got another method which wants to add Students to a list. If the method parameter takes a List<Student>
then it can't take a List<People>
even though that should be fine. So, you implement it as taking a List<? super Student>
e.g.
public void listPopulatingMethod(List<? extends Student> source, List<? super Student> sink) { for (Student s: source) { sink.add(s); } }
This is the heart of PECS, which you can read about in much greater detail elsewhere... What is PECS (Producer Extends Consumer Super)? http://www.javacodegeeks.com/2011/04/java-generics-quick-tutorial.html
List<? super Person> list = new ArrayList<>(); list.add(clarissa); // clarissa is an instance of Student class
The reason why you can do things above, Suppose there is a Person class, Student class extends Person. You can think List means that in this List all elements is of class Person or superclass of Person, so when you add Person class instance or subclass of Person class, the implicit casting will happen. e.g. Person person = new Student();
public static void main(String[] args) { ArrayList<? super Person> people = new ArrayList<>(); people.add(new Person()); people.add(new Object()); // this will not compile }
But if you add Object class instance into the list, explicit casting or down casting is required and the compiler does not know whether say (Person) Object can be successful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With