Generic concept of extends and super - java

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

Related

Why does this compile? Java

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 !

Why is it necessary to extend in case of read and super in write in generics wildcards?

I am having a hard time to understand the concept of generics wild cards.
As per my understanding <?> type unknown is introduced to resolve the co-variance not supported in generics and it should fit any type of collection and <?extends T> means that you can have collection of types T or the class which extends T.<?super T> means you can have collection of types T or super(s) of T.
Please correct me, if the above is wrong.
When I try to write it like this:
import java.util.*;
public class Gclass {
static Gclass t;
public void write(List< ?super String > lw){
lw.add("b");
}
public void read(List< ? extends String> lr){
String s=lr.get(2);
System.out.println(s);
}
public static void main(String[] args) {
t=new Gclass();
List<String> l=new ArrayList<String>();
l.add("a");
l.add("");
System.out.println(l);
t.write(l);
System.out.println(l);
t.read(l);
System.out.println(l);
}
}
It works but my places of doubt are:
As per my understanding both (extends and super) includes the type declared, so in this particular case as my List is of type String. I could interchange the extends and super, but I get compilation error?
In case of write ? super Object is not working? It should work as it is super of String?
I did not check for read as String can not be extended, but I think I'm also missing a concept here.
I've read all answers on SO related to this problem, but am still not able to have a good understanding about it.
String is indeed a bit of a bad example as it is a final class, but consider something like Number instead.
If a method takes a parameter of type List<? extends Number> then you can pass it a List<Number> or a List<Integer> or a List<BigDecimal> etc. Within the method body it is therefore fine to take things out of the list (as you know they must be instances of Number) but you can't put anything in because you don't know whether or not it's safe (the compiler can't let you risk putting an Integer into a List<Float>, for example).
Conversely if the method takes List<? super Number> then you can pass it a List<Number> or List<Object> - you can't take anything out of this list because you don't know what type it is*, but you do know that it'll definitely be safe to put a Number in.
* technically you can take things out but the only type you can assign them to is Object
As per my understanding both(extends and super) includes the type declared(String here), so in this particular case as my List is of type String... I could interchange the extends and super but i get compilation error?
You're right that both ? extends String and ? super String includes String. But you are missing the point that, ? super String also includes CharSequence, Object, which is not in bounds of ? extends String. You can add a String to a List<? super String>, b'coz whatever type that list is of, it can definitely refer to a String. But, you cannot add say an Integer to a List<? extends Number>, because the list can be a List<Float> actually.
In case of write ? super Object is not working? It should work as it is super of String?
Object is a super class of String will fit in where you have ? super String, and use Object for that. So, ? super String can capture Object, but ? super Object cannot capture String, as String is not a super type of Object. Think of it like this: "Actual type replaces the ?, and it must satisfy the rules attached to that ?.
List<? super String> means that lw holds a value of List with type argument which is String or it's superclass, so you can add a String value "b" (because it can be casted to list's type argument).
List<? extends String> means that lw holds a value of List with type argument which is String or it's subclasses, so values from lw can be casted to String.

Wildcard java extends

Consider the following code:
List<? extends Integer> lst= new ArraList<Integer>();
lst.add(5);//Compile error
lst.get(5);//OK
In the second string we have compile error, because we must guaranted have method add(int) in all potencial subtypes of List<? extends Integer> and compiler know that its null only, third string returns unknow type and compiler cast him to Object, isnt it?
PECS. Producer extends, Consumer super.
List<? super Integer> lst= new ArrayList<Integer>();
lst.add(5);// No Compile error
The list is a consumer now, you put objects into it...
Also this
lst.get(5);
Is a different cake... You provide the Integer index of which you want to get... (Not to mention what Sotirios mentioned: the return type will be Object in this case. Also, in this role, the list is a provider...)
Once you have a List<? extends Integer>, the compiler doesn't know whether it's Integer or a subtype. The compiler can't ensure the type safety of adding anything except null (really, passing anything to a method taking a generic type parameter, including the add method), so it disallows it. This occurs despite the fact that Integer is final and no subclasses are allowed.
For the get method, the compiler knows that the return type is some kind of Integer, so it allows the call and places an implicit cast to Integer.
You cannot add anything except null to a List bounded by a wildcard because you never know the underlying type.
List<? extends Integer> lst= new ArrayList<Integer>();
lst.add(5); // Compile error
lst.get(5); // 5 is just the index
You can however get an element because you know it must be an Integer (in this case).
It's hard to explain with Integer because it's a class that cannot be extended. But take this
public class Foo {}
public class Bar extends Foo {}
public class Zoop extends Foo {}
What could you add to
List<? extends Foo> fooList = new ArrayList<Zoop>(); // could come from a method
?
The list is declared as ? extends Foo even though the runtime object's generic type is Zoop. The compiler therefore cannot let you add anything. But you are guaranteed to be operating on Foo objects, so you can retrieve them fine.

Java generics confusion

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.

Generics (List) typing question

I am trying to use a common technique to create objects from Xml. (Xml is legacy, so although there are already libraries to do this, it seemed faster to write this myself.)
I don't understand the compiler's complaint about the generic usage. Code sample:
public void createObjects() {
List<Object1> objectOnes = new ArrayList<Object1>();
List<Object2> objectTwos = new ArrayList<Object2>();
parseObjectsToList("XmlElement1", objectOnes);
parseObjectsToList("XmlElement2", objectTwos);
}
private void parseObjectsToList(String xmlTag, List<? extends Object> targetList) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(xmlTag);
targetList.add(newObj)
/* compiler complains: "The method add(capture#2-of ? extends Object) in the type List<capture#2-of ? extends Object> is not applicable for the arguments (Object)"
*/
/* If I change method signature to parseObjectsToList(String xmlTag, List targetList)
it works fine, but generates compiler warning about raw type */
}
Thanks for any enlightenment on the subject!
The problem you are running into is that, with the bounded wildcard that you have defined, you will be unable to add any element to the collection. From this tutorial:
List<? extends Shape > is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard.
There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method
All a wildcard type means is that the actual type parameter T of the List that you pass as the second argument to parseObjectsToList is going to be a subtype of Object. It does NOT mean that the same List will be parameterized with different types.
So now you have a List<T> (called targetList) and you are trying to call targetList.add(Object). This is illegal because Object is not necessarily a subtype of T.
Because you are adding to the List rather than extracting elements from it, use List<Object> and make sure that's exactly what you pass in.
Using a List<Object> will work, but you might want keep your more precisely typed List<Object1> and List<Object2> for type-safety elsewhere. In that case, you'll need to check the type of each object before adding it to the List.
private void parseObjectsToList(String tag, List<T> list, Class<? extends T> c) {
// read Xml and create object using reflection
Object newObj = createObjectFromXml(tag);
list.add(c.cast(newObj)) ;
}
The cast() operation is a reflective equivalent to the static cast operator: (T) newObj
Using the altered method would look something like this:
parseObjectsToList("XmlElement1", objectOnes, Object1.class);
Think about what you are asking the compiler to do:
Given a list of "something that is a subtype of Object
Let me insert an Object into it
This doesn't make sense. Suppose your list is a list of Integer. Suppose that createObjectFromXml returns a String. It wouldn't make sense to allow inserting a String into a list typed for Integers.
So, your options are either to make your List a List<Object> or to find some way to make createObjectFromXml return a specific type, that you can then tie to the type of your list.

Categories