I was taught that the following compiles:
Collection <? extends T> collection;
List<T> list;
collection = list; // Compiles
With the reason that "that's how Java developers defined it". I would like to know the rationale behind it. It compiles but can make problems during runtime (e.g. we wouldn't be able to add any objects to collection).
Any clarification would be appreciated.
Edit: I am referring to the fact that an Object of generic type ? extends T is pointing to an Object of generic type T. It seems rather counterintuitive.
Edit: I am referring to the fact that an Object of generic type ? extends T is pointing to an Object of generic type T. It seems rather counterintuitive.
To understand this, you have to understand what exactly a Collection<? extends T> is.
It is: a Collection with elements of a specific, but unknown type (indicated by the ?) that extends T.
Note that it is not a Collection of objects of arbitrary (and possibly different) types that extend T (this is a misconception that many developers have about generic wildcards).
It's perfectly OK that you can assign a List<T> to a variable of type Collection<? extends T>, because List is a subtype of Collection, and the elements of a List<T> are indeed of the type ? extends T. (In this particular case, the actual type is the type T itself, but that still matches "some unknown type ? that extends T").
Note that using the wildcard actually throws away information about the exact type of the elements of the collection - it makes Java forget the exact type, and only makes it remember that it's an unknown type that extends T.
You cannot add anything to a collection of a wildcard parameterized type such as a Collection<? extends T>, precisely because the information about the exact type of the elements is missing. If you would try to add an element to such a collection, there's no way for the compiler to check if the type of the element you're adding is the right type.
If you try to call add() on a Collection<? extends T> you will get a compile error that says that the type of the object you're adding is not of the type "capture of ... of ? extends T". That basically means: "I cannot check that the object you're trying to add is of the unknown type ? extends T".
The type can also not be checked at runtime because of type erasure: type arguments are a compile-time only thing in Java, at runtime they don't exist anymore so also then there's not enough information to check that the element you're adding is of the right type.
It is because Collection is an interface. List is also an interface which extends Collection interface.
By Object oriented principles, parent class reference can hold the child class reference!
For e.g. If I have below
class Parent {
//Some code
}
class Child extends Parent {
//Some code
}
I can do this
Parent parentObject = new Child();
parentObject.childMethod(); or parentObject.parentMethod();
https://docs.oracle.com/javase/8/docs/api/java/util/List.html This docs can help !
Related
Lets assume that we have
class A implements Comparable<A>
And
class B extends A
Now, I would like to have generic method that finds biggest object from collection, using compareTo method.
If I declare this method like so:
public static <T extends Comparable<T>> T max(List<T> list)
It won't work for argument List<B> list because B does not parameterize Comparable with type B. For that method to work, it seems natural for me to change
Comparable<T>
To
Comparable<? super T>
So method after that change will look like this:
public static <T extends Comparable<? super T>> T max(List<T> list)
Now passing argument List<B> list works fine and gives no errors. Fine. But when i change method signature to
public static <T extends Comparable<T>> T max(List<? extends T> list)
It also works fine for argument List<B> list! And my question is WHY. Could someone explain that to me? Thanks :)
List<? extends T> means "a List. We don't know a List of what, but whatever it is, we do know that it extends T or is T itself."
One effect of this is that when we take an element from the List, the compiler will know it as of type T. The actual runtime class of the item may be a subtype of T, as is always the case with polymorphism, but no one cares about that. We know it can be stored in a variable type T.
Another effect is that we can't put any element in the List, because we don't know of what the List is. So whatever we'd try to put inside, we don't know if it is of acceptable type, therefore we can't. But when finding the max it doesn't matter: we won't be adding anything anyway.
Therefore, a List<B> can also be taken as a List<? extends A> if needed (suddenly decide that we don't know don't care of what the List is, but whatever it is it extends A.) This is helping because then it allows to match the method signature, taking T as A.
It's not the same thing.
In the first example, you're saying, "For some type T, such that I can cast any T to a Comparable of a supertype of T, then I'll accept a list of T and return an instance of T."
When you call the method with a List<B>, since every B is a Comparable<A>, it's therefore true to say: "I can cast any B to a Comparable of a supertype of B", and the type parameter T resolves to concrete type B.
In the second example, you're saying, "For some type T, such that I can cast any T to a Comparable of T, then I'll accept any list of T or any list of any subtype of T, and return an instance of T."
When you call this one with A List<B>, then the type parameter T resolves to A; of course any A is a Comparable<A>, and you can pass a List<B> into the method because a list of B is indeed a list of a subtype of A.
So that's why both will compile.
But you're asking about the purpose of being able to say List<? extends T>. This lets you remain open to receiving collections (or other parameterized types) of subclasses of the class you're working with. This is usually what you want when you're going to receive objects of a certain type and do something with them, and usually, you'd be equally happy to do that to a subtype (since a subtype should respect the same contract).
The super keyword means that you can work with objects whose type parameter is a superclass of your type parameter. The typical case is when you want to add an object to a collection. Suppose that T here resolves to concrete type B. If you received a List<A> myList then List.add(B) would be legal, because it's OK to add a B to a List<A>. It's also useful if your method returns, say, a Set<T> - in that case, the calling code can't assign it to a Set<A> even though everything in the set is an A, but if you instead return Set<? super T> then it can. In your case, you're going to call Comparable<A>.compare(B) and here, similarly, it's legal to pass an instance of B to Comparable<A>.compare(A).
There's a mnemonic for this, PECS "Producer Extends, Consumer Super" discussed here: What is PECS (Producer Extends Consumer Super)?
This question is specifically about nested lists which is a static field of the generic class. If I am maintaining the logs in a static variable inside my class using the code:
static List<List<? extends Number>> myLog = new ArrayList<List<? extends Number>>();
myLog is a list of list whose element is of type that extends Number. Here, instead of extending Number, I might want to use a generic type <? extends T>.
I understand that type variables exist only at compile time, and all instances of a generic class has the same run-time class, due to type erasure. Then, how is the nested list (List interface) within the top-level list (ArrayList) is determined at compile time about its actual implementation type (ArrayList,LinkedList, ...)? I find it a bit complex, as a couple of things are happening at the same time. Any help is appreciated.
EDIT: So, for example, in my code I have a method sortList which sorts a list of numbers, and it will also log that list of numbers in myLog list, like:
public void sortList(List<? extends Number> inList) {
// do something
myLog.addLast(inList);
// do some more
}
Now, instead of using <? extends Number>, I might want to use <? extends T> in a generic class. So when creating the static field myLog, when type erasure happens, how does compiler determine the type of the nested list. I hope I am a bit clearer this time.
(as said in comments)
Type erasure hapens after the type has been determined and check.
In your code you described the type of myLog and the type of inList, so the compiler can determine at compile time if the types matches (and they do in both case) and thus, enforce the types.
Then type erasure happens, and types are lost for runtime (if you get myLog as a List you can add whatever you want, juste like if you described it as static List myLog).Well they're not completely lost: meta datas/refelction can give you these inforamtions, but they're not enforced anymore, like the compiler did.
What is the concept behind the Generic extend that why is it not allowed to modify the
list; why does it throw a compile time error when I add a string to list , since String extends Object and should be legal.
If this gives compilation error , then what is the use of that list that is created then.
List<? extends Object> ls=new ArrayList<String>();
ls.add("asd"); // compilation error
And it compiles in the case of super.
List<? super Integer> ls1=new ArrayList<Object>();
ls1.add(1);
I have read Kathy Sierra and Javadoc, but am not able to understand what this means. Please give me a detailed explanation with examples to understand this.
You can't add Strings to a List<? extends Object> because ? could be anything.
If you want to put things into a list, its type parameter should be a superclass of the type you want to put in. (This includes the type itself.)
If you want to get things from a list, its type parameter should be a subclass of the type you want to take out. (This includes the type itself.)
This can be remembered with the acronym PECS - producer-extends, consumer-super.
Compiler does not care try to analize what actual generic type of list is, it checks only ls declared generic type. It is the same as here
void add(List<? extends Object> ls) {
ls.add("1");
...
ls can be eg a list of Integers, you cannot add "1" to it.
Similar explanation applies to super
Why is the following legal when String & Integer are not super classes of Object ?
List<? super Object> mylist = new ArrayList<Object>();
mylist.add("Java"); // no compile error
mylist.add(2);
I'm aware that wild card guidelines use lower bounded wild cards and super for 'out' variables but it seems that Object doesn't function as a 'lower bound' in this case.
Also is this the only way to allow addition of any type into a list ?
It's really simple. Remember that in Java, an instance of a subtype is also an instance of its supertypes.
Look at the signature of add
public boolean add(E e)
That means whatever you pass something whose type is E or any subtype of E.
You have a List<? super Object>. So you can pass to myList.add() anything whose type is ? super Object (an unknown type which could be Object or supertype thereof) or any subtype thereof.
Is Integer a subtype of all types contained by ? super Object? Of course. Integer is a subtype of Object, which is a subtype of all types contained by ? super Object (of course, in this case, only Object satisfies this).
You're confusing the type parameter with the things you can pass to methods. The type argument of List<? super Object> is an unknown type that is a supertype of Object, so Integer or String cannot be the actual type parameter. In fact, in this case the only valid actual type argument would be Object. But what you're asking when you pass something to the method is, is the thing I'm passing a subtype? And the answer is yes.
I agree that it's confusing, but here's what's happening.
In this line of code:
List<? super Object> mylist...
You're saying that myList is a List, where each element can be of a type that is Object or a superclass of Object. However, you're only declaring the type of myList here.
What the wildcard does is restricts your implementation of myList.
Then, you do this:
List<? super Object> mylist = new ArrayList<Object>();
Now what you're doing is instantiating an ArrayList<Object>. Your lower bound wildcard is used to check that this is valid. It is valid, because Object matches ? super Object. At this point, you have a List<Object> and your ensuing method calls are permitted.
It's because Object is a superclass for Integer and String. You're interpreting the generic relationship the other way around.
Edit
Think about this situation:
List<? extends myClass> listOfMyClass = new ArrayList<Object>();
In this case, you'll end up with a list of Object type elements but that have to respect the restriction added by the declaration of the listOfMyClass list.
You'll be able to add any object that belongs to the myClass hierarchy to the list. The ArrayList that's implementing the List interface will hold (and return) Object type elements when requested.
Of course, you can define this:
List<? extends myClass> listOfMyClass = new ArrayList<mySuperClass>();
As you might now, the ArrayList must contain objects with the same type or a supertype of myClass and, in this case, that's the mySuperClass. This list will return mySuperClass objects qhen requested.
Taking ClassX as a class that does not belong to the mySuperClass hierarchy, the following line won't compile:
List<? extends myClass> listOfMyClass = new ArrayList<ClassX>();
That's because ClassX is not a superclass of myClass.
I am a bit of confused about java generics
Here is the code
class Base{}
class Derived extends Base{}
WE can instantiate a list like this
List<? extends Base> list = new ArrayList<Base>();
Why cannot I add a a new item like this
list.add(new Base());
So user cannot use "add" method as far as a wildcard ? in the genetics type?
Thanks
PECS - producer extends, consumer super.
If you replace extends with super, you can add new Base().
List<? extends Base> means "a list that holds instances of any subclass of Base (or Base itself). But it cannot hold instances of two different subclasses.
If you want your list to hold Base and Derived, then use List<Base>. But note that it cannot be later cast to List<Derived>
Just make it
List<Base> list = new ArrayList<Base>();
You shouldn't use wildcards when you know the actual type... just when you're being provided with something with an unknown type.
In such cases, ? extends Base means that the List is only allowed to contain some specific subtype of Base, but you don't know which subtype that is. Because of that, you can't add anything but null to the list.
You can try reading ? as something:
List<? extends Base>
This is "List of something that extends Base". So it is clear that you cannot add a Base (just as you cannot add an Object to a List<String> even when String extends Object.
What you can do in your case is:
List<? super Base>
This is "List of something that is extended by Base". So you can add a Base there (just as you can add a String to a List<Object>, because Object is extended by String.
I think this is a design of Java Generics. The wildcard ? extends Base is compiled to mean that the collection reference can point to a collection object that can hold any ( and all ) types that extend Base.You can write like this as well :
List<? extends Base> _listBaseSubtypes = new ArrayList<Derived>();
Now , with the above line , if you think about it , the below will be obviously an error :
_listBaseSubtypes.add(new Base());
I think Java designers decided to allow the first line of code as valid. In order to avoid the runtime error that the second line of code can cause , it is caught at compile time.
Having said that , the question that comes to mind is : What type of object should be allowed to be added into the collection , given the fact that the actual collection object can be a collection of 'any' derived type ?
Because you can derive as many types as you want , and there cannot be found a single type that is assignment compatible with the type held in the actual collection object ( remember , the collection object could be declared to hold 'any' derived type ) , the simple answer to the question is : None. So , you cannot add any object into the collection through the add interface, because for any object that you may try passing into the add method , there will be complier objection raised on the reason that this type is not compatible with the type that the actual collection object holds.