Java generics list - java

I know following is true.
List<? extends Number> aNumberSuperList = new ArrayList<>();
List<? extends Integer> aIntegerSuperList = new ArrayList<>();
aNumberSuperList = aIntegerSuperList;
But what type of objects can be added to such a list.
List<? extends Number> aNumberSuperList2 = new ArrayList<>();
aNumberSuperList2.add(???)

Only null can be added to a List<? extends Number>. This is because the exact type parameter isn't known. A List<Integer> could be assigned to this list, and so could a List<AtomicLong>. The compiler must prevent a call to add with anything but null, to avoid the situation where a Integer might be added to something that's referred to as a List<? extends Number>, but is really a List<Double>, for example. null is the only safe thing to add here, because it can be of any type.

Related

What is the point of stating that a type wildcard must extend Object? [duplicate]

I've seen the wildcard used before to mean any object - but recently saw a use of:
<? extends Object>
Since all objects extend Object, are these two usages synonymous?
<?> and <? extends Object> are synonymous, as you'd expect.
There are a few cases with generics where extends Object is not actually redundant. For example, <T extends Object & Foo> will cause T to become Object under erasure, whereas with <T extends Foo> it will become Foo under erasure. (This can matter if you're trying to retain compatibility with a pre-generics API that used Object.)
Source: http://download.oracle.com/javase/tutorial/extra/generics/convert.html; it explains why the JDK's java.util.Collections class has a method with this signature:
public static <T extends Object & Comparable<? super T>> T max(
Collection<? extends T> coll
)
Although <?> is supposed to be a shortcut for <? extend object>, there is a tiny difference between the two.
<?> is reifiable while <? extend object> is not. The reason they did this is to make it easier to distinguish reifiable type. Anything that looks like <? extends something>,<T>,<Integer> are nonreifiable.
For example, this code would work
List aList = new ArrayList<>();
boolean instanceTest = aList instanceof List<?>;
but this gives an error
List aList = new ArrayList<>();
boolean instancetest = aList instanceof List<? extends Object>;
for more info read Java generics and collections by Maurice Naftalin
<?> is a shorthand for <? extends Object>.
You may read below shared link for more details.
<?>
"?" denotes any unknown type, It can represent any Type at in code for. Use this wildcard if you are not sure about Type.
ArrayList<?> unknownList = new ArrayList<Number>(); //can accept of type Number
unknownList = new ArrayList<Float>(); //Float is of type Number
Note: <?> means anythings. So It can accept of Type which are not inherited from Object class.
<? extends Object>
<? extends Object> means you can pass an Object or a sub-class that extends Object class.
ArrayList<? extends Number> numberList = new ArrayList<Number>(); //Number of subclass
numberList = new ArrayList<Integer>(); //Integer extends Number
numberList = new ArrayList<Float>(); // Float extends Number
T – used to denote type
E – used to denote element
K – keys
V - values
N – for numbersRef:

Using addAll() on List<? extends E> a and b doesn't work

Why doesn't this compile?
List<? extends Number> a = new ArrayList<>();
List<? extends Number> b = new ArrayList<>();
a.addAll(b);
Because it wouldn't be safe.
List<? extends Number> should be read as some list where the element type extends Number. So in runtime a could be a List<Long> and b could be a List<BigInteger>. In that case, a.addAll(b) would mean "add all BigIntegers to the list of Longs" which, if allowed, obviously wouldn't be type safe.
https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Iterables.html
What really worked for me was Guava:
com.google.common.collect.Iterables.concat(...)
List<? extends Number> means
Items in this List have all the same class. Not only do they extend
Number, but are all the same type.
Due to type erasure during compile, the byte code interpreter does not know (and cannot infer), whether the ? of a and the ? refer to the same class.

Fancy generics capture collision

Please give me a hint as to what is going on here:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
a.addAll(b); // ouch! compiler yells at me, see the block below:
/*
incompatible types
found : java.util.List<capture#714 of ? extends java.lang.Number>
required: java.util.List<java.lang.Number>
*/
This simple code does not compile. I vaguely remember something related to type captures, like those should be mostly used in interface specs, not the actual code, but I never got dumbfounded like that.
This of course might be fixed brute-forcefully, like that:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
#SuppressWarnings({"unchecked"})
List<Number> aPlain = (List<Number>) a;
#SuppressWarnings({"unchecked"})
List<Number> bPlain = (List<Number>) b;
aPlain.addAll(bPlain);
So, do I really have to either give up captures in the declaration (the capture came to me from an interface, so I'll have to change some API), or stick with type casts with suppression annotations (which generally suck and complicates code a bit)?
You have essentially two lists of possibly different types. Because ? extends Number means a class which extends Number. So for list a it can be classA and for list b it can be for example classB. They are not compatible, they can be totally different.
The problem is that if you use List<? extends Number> you could actually do:
List<? extends Number> a = new ArrayList<Integer>();
List<? extends Number> b = new ArrayList<Double>();
a.addAll(b); //ouch, would add Doubles to an Integer list
The compiler can't tell from List<? extends Number> what the actual type parameter is and thus won't let you do the add operation.
You also shouldn't cast the lists to List<Number> if you get them as a parameter, since you could actually have a list of Integer objects and add Double objects to it.
In that case you better create a new List<Number> and add the objects from both lists:
List<Number> c = new ArrayList<Number>(a.size() + b.size());
c.addAll(a);
c.addAll(b);
Edit: in case you create both lists locally, you would not neet the ? wildcard anyway (since you'd always have List<Number>).
PECS (producer-extends, consumer-super)
You cannot put anything into a type declared with an EXTENDS wildcard
except for the value null, which belongs to every reference type
You cannot get anything out from a type declared with an SUPER
wildcard except for a value of type Object, which is a super type
of every reference type
Don't use the ? wildcard. It means "Some specific type I don't know", and since you don't know the type, you can't add anything to the list. Change your code to use List<Number> and everything will work.
This is fast becoming the most frequently asked Java question, in a hundred variations...
The thing is:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
could also be read as:
List<x extends Number> a = new ArrayList<Number>();
List<y extends Number> b = new ArrayList<Number>();
How should the compiler know that x and y are the same?

<? super E> and <? extends E> for List

Having the following simple class structure:
class A {
}
class B extends A {
}
class C extends B {
}
I'm creating an ArrayList to keep objects of the earlier created classes:
List<? extends A> list1 = new ArrayList<A>();
List<? extends B> list2 = new ArrayList<B>();
List<? extends C> list3 = new ArrayList<C>();
List<? super A> list4 = new ArrayList<A>();
List<? super B> list5 = new ArrayList<B>();
List<? super C> list6 = new ArrayList<C>();
To each of those lists I'm trying to add 1 object of each earlier created class: A,B,C. The only possible combination is:
adding object of class A,B,C to list4
adding object of class B and C to list5
adding object of class C to list list6.
The rest of the tries gives compiler errors, such us:
The method add(capture#1-of ? extends
A) in the type List is not applicable for the
arguments (A)
Why can't I add any object of class A,B,C to list1/2/3?
Why e.g. list4 accepts objects of classes A,B,C if they are supposed to be a super class of class A, as the list4 is defined?
"? extends A" means "some type derived from A (or A itself)". So for instance, a List<ByteArrayOutputStream> is compatible with List<? extends OutputStream> - but you shouldn't be able to add a FileOutputStream to such a list - it's meant to be a List<ByteArrayOutputStream>! All you know is that anything you fetch from the list will be an OutputStream of some kind.
"? super A" means "some type which is a superclass of A (or A itself)". So for instance, a List<OutputStream> is compatible with List<? super ByteArrayOutputStream>. You can definitely add a ByteArrayOutputStream to such a list - but if you fetch an item from the list, you can't really guarantee much about it.
See Angelika Langer's Generics FAQ for much more information.
The type definition List<? extends A> is not usable for a mutable List - the explanation given in Java generics Java Generics Pdf is
The add() method takes arguments of type E, the element type of the
collection.
When the actual type parameter is ?, it stands for some unknown type.
Any parameter
we pass to add would have to be a subtype of this unknown type. Since we
don’t know
what type that is, we cannot pass anything in.
However, when the typedef is List<? super A> then the type parameter ? is implicitly typed.
List<? extends A> list1
It is a list, whose type element could be any unknown subclass of A. For example, it could be a D subclass. Therefore, you can't add anything to it, it could be wrong...
List<? super A> list4
It is a list, whose type element could be A, or a superclass of A (does not exist in that case, except Object). Therefore, you can add A objects to it, or any subclass of A such as B or C.
It doesn't work that way.
You should use <? extends T> when you create function which argument is collection of unknown subtype of some type, and you want to fetch objects from that collection:
int sum(Collection<? extends Integer> collection) {
for (Integer : collection) {
// do sth
}
// ...
}
You cannot add new items to this collection, because you don't know which concrete type is this collection holding. All you know is that that type extends Integer.
You use <? super T> when you want to add new items of type T to collection and return that collection, but then you cannot guarantee what you can retrieve from it and you have to cast result of get() or check its type. You can safely add items of type T and subtypes, and retrieve items of type T.
List<? super B> allows you to use Lists of any supertype of B, i.e. list5 = new ArrayList<A>();
or list5 = new ArrayList<Object>();
You can safely add B (and subtypes) to every list that use supertypes of B, but you can not add any supertype of B. Imagine this:
public void myAdd(List<? super B> lst) {
lst.add(new Object()) //this is a supertype of B (compile time error)
}
...
ArrayList<A> list = new ArrayList<A>();
myAdd(list); //tries to add Object to a list of type A

Java Generic List<List<? extends Number>>

How come in java we cannot do:
List<List<? extends Number>> aList = new ArrayList<List<Number>>();
Even though this is OK:
List<? extends Number> aList = new ArrayList<Number>();
Compiler error message is:
Type mismatch: cannot convert from ArrayList<List<Number>> to List<List<? extends Number>>
In Java, if Car is a derived class of Vehicle, then we can treat all Cars as Vehicles; a Car is a Vehicle. However, a List of Cars is not also a List of Vehicles. We say that List<Car> is not covariant with List<Vehicle>.
Java requires you to explicitly tell it when you would like to use covariance and contravariance with wildcards, represented by the ? token. Take a look at where your problem happens:
List<List<? extends Number>> l = new ArrayList<List<Number>>();
// ---------------- ------
//
// "? extends Number" matched by "Number". Success!
The inner List<? extends Number> works because Number does indeed extend Number, so it matches "? extends Number". So far, so good. What's next?
List<List<? extends Number>> l = new ArrayList<List<Number>>();
// ---------------------- ------------
//
// "List<? extends Number>" not matched by "List<Number>". These are
// different types and covariance is not specified with a wildcard.
// Failure.
However, the combined inner type parameter List<? extends Number> is not matched by List<Number>; the types must be exactly identical. Another wildcard will tell Java that this combined type should also be covariant:
List<? extends List<? extends Number>> l = new ArrayList<List<Number>>();
I'm not very familiar with Java syntax but it seems that your issue is this:
Covariance & Contravariance
You should definitely use the ? type wildcard when appropriate, do not avoid it as a general rule. For example:
public void doThingWithList(List<List<? extends Number>> list);
allows you to pass a List<Integer> or a List<Long>.
public void doThingWithList(List<List<Number>> list);
allows you to only pass arguments declared as List<Number>. A small distinction, yes, but using the wildcard is powerful and safe. Contrary to how it may seem, a List<Integer> is not a subclass, or is not assignable, from List<Number>. Nor is List<Integer> a subclass of List<? extends Number, which is why the code above does not compile.
Your statement does not compile because List<? extends Number> is not the same type as List<Number>. The former is a supertype of the latter.
Have you tried this? Here I'm expressing that the List is covariant in its type argument, so it will accept any subtype of List<? extends Number> (which includes List<Number>).
List<? extends List<? extends Number>> aList = new ArrayList<List<Number>>();
Or even this. Here the type parameter for the ArrayList on the right-hand side is the same as the type parameter on the left-hand side, so variance is not an issue.
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
You should be able to just say
List<List<Number>> aList = new ArrayList<List<Number>>();
I tend to avoid the ? type wildcard whenever possible. I find that the expense incurred in type annotation is not worth the benefit.
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
aList.add(new ArrayList<Integer>());

Categories

Resources