Can the Object class really be a lower bound? - java

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.

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 !

generic wild card covariance and arraylist add function behaviour

EquilateralTriangle is a subtype of Triangle.
List<? super EquilateralTriangle> equilateralTriangle =
new ArrayList<Triangle>(Arrays.asList(new Triangle(), new Triangle()));
equilateralTriangle.add(new Triangle()); // doesn't work
equilateralTriangle.add(new EquilateralTriangle()); // works
As far as I know List<? super EquilateralTriangle> is the declaration and with new ArrayList<Triangle> we are telling something concrete to the compiler that my arraylist is of type triangle. Then why does compiler complain on the second line? I'm inserting the same value as I defined in the type parameter defined while instantiating arraylist.
Java arraylist definition says this:
class ArrayList<E> extends AbstractList<E>
public ArrayList(Collection<? extends E> c)
and boolean add(E e); for ArrayList.
So, the type E can be infered as Triangle. Then add must not complain as I'm passing the Triangle as I did in ArrayList constructor. What is missing in my understanding?
Take a look at the following statements. Obviously, adding a String works whereas adding an Object causes a compile-time error.
List<String> items = ...;
items.add(new String()); // works
items.add(new Object()); // error
Now, let's use a wildcard as in your question:
List<? super String> items = ...;
In this variable declaration, the ? stands for an unknown type, that is either String or a base class of String. The type is not deduced/inferred. It is just an unknown type. The compiler must assume, that ? super String can be String. And as we have seen above, you are not supposed to add Objects into a list of Strings.
nosid's answer explains the contravariance part. I'll just address this question:
why does compiler complain on the second line? I'm inserting the same value as I defined in the type parameter defined while instantiating arraylist.
I could ask you the same question about this code:
Object object = new ArrayList<Triangle>();
object.add(new Triangle());
Why doesn't this work? Doesn't the compiler know that object is an ArrayList<Triangle>?
The answer is no. It doesn't matter to the compiler what type was instantiated. What matters is the type of the variable that you call add on.

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 with extends

Code:
List<? extends Integer> ints= new ArrayList<Integer>();
ints.add(new SomeType());
I'm trying to reason why we cant add to ints formally. Please check the correctness.
Compiler always matched question mark to anonymous type CAP#n, where n is the ordinal number of wildcard declaration in the source code. From the fact that this wildrcard with extends implies that compiler internally assigned CAP#1 (in that case) just to anonymous type of null. But i'm not sure in this reason. Consider
List<? super Integer> ints= new ArrayList<Integer>();
ints.add(new Object());//error
In this case we have that compiler internally creates a new anonymous type, who marked CAP#2, such that only instance of all Integer's supertype is "instance of" CAP#2.
Question Do I understand the principle of wildcard's working correct now?
Let's try to look at the presented problem in different view, from java.util.List
public interface List<E> extends Collection<E> {
*
*
boolean add(E e);
*
*
}
When you specify List<? extends Integer>, the argument for add() becomes '? extends Integer'. From that description, the compiler cannot know which specific sub type of Integer is required there, so it won't accept any type of Integer.
The usage of List<? super Integer> tells to compiler that the it's possible to add to the list everything that is super type of Integer, addition of other types will violate static type safety.
You can thus begin to think of subtypes and supertypes bounds in terms of how you can "write" (pass into a method) to a generic type, and "read" (return from a method) from generic type.
Basically your technical description is right but I think that my explanation is more reasonable from static type safety point of view.
Hope it helps you.
Let's ignore the fact that Integer is final for purposes of this discussion.
When you give a variable the type of List< ? extends Integer >, the compiler does not allow you to call any method of List which has the generic type parameter as an argument to a method. These parameters are in what is known as contravariant position, and if the compiler would allow you to do what you are trying to do, Java's type system would be even more unsound than it already is. All the compiler knows is that the element type of the List is some unknown subtype of Integer, internally called CAP#1. Now attempting to call add( CAP#1, int ) with anything at all as first parameter will fail. The only exception is null, simply because unlike any other value in Java, null is a member of every reference type, so it must be a member of CAP#1. Note that the compiler will allow you to call any method of a List< ? extends Integer > that has no generic type inputs but possibly produces generic type outputs.
Contrary to the answer given by #Maxim Kirilov, when you give a variable the type of List< ? super Integer >, the compiler does not allow you to add any supertype of Integer. All it knows is that the unknown type is some supertype of Integer, internally called CAP#2, and that therefore anything which is an Integer or any subtype S of Integer can be added to the list (because no matter what CAP#2 turns out to be, S is a subtype of Integer which is a subtype of CAP#2, so add( CAP#2, int ) will accept an S in the first parameter).
By contrast, you are trying to call the method with an Object, which is not a subtype of Integer. The compiler rejects an attempt to pass an Object where a CAP#2 is required (as explained above).

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.

Categories