Can I use generics wildcard in List declaration? - java

Consider the following codes:
class Super {}
class Sub extends Super {}
class Test {
public static void main(String[] args) {
List<? extends Super> list = new ArrayList<Sub>(); //1
list.add(new Sub()); //2
}
}
Line 1 compiles successfully, but line 2 fails compilation:
The method add(capture#2-of ? extends Super) in the type List<capture#2-of ? extends Super> is not applicable for the arguments (Sub)
My questions are:
1) Why does line 1 successfully compile?
2) Is line 1 a good practice in declaring a List (or other collections)?
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1?
4) Eclipse's autocompletion says only "null" elements are allowed in list now. Why?
Thanks a lot!

1) Why does line 1 successfully compile?
The first line compiles because List<Sub> is subclass of List<? extends Super> and that can only be true if List does not allow you to add any new members to it.
? means that you don't exactly know that it is a List<Sub> or List<Sub1>, so it is unsafe to allow adding of new elements into the list and so it does not allow it.
2) Is line 1 a good practice in declaring a List (or other
collections)?
If you already know that it is going to be List<Sub> then I don't find any use, but wildcards are used much when you are passing the List around to other classes like Utilities.
3) Why does line 2 fail compilation, after list was declared to be
type Sub in line 1?
Because as I already explained it is unsafe to add any element to list when you dont know the exact type.
4) Eclipse's autocompletion says only "null" elements are allowed in
list now. Why?
Because null is of every reference type, this is why you can assign any object a value of null.
Always remember PECS (Producer Extends Consumer Super) rule by Josh Bloch when working with generics
Good References:
Difference between ? extends and ? super
When do we require <? extends T> as compared to <T>

Capturing declarations as in line 1 are good in method arguments. See Collection.addAll(Collection<? extends E>). If you need a list with something extends Super, just use List<Super>.

You have declared your list as, it can be assigned to any sub type of Super class. So, assigning a Sub type list is OK, Compiler allows to compile. But that doesn't mean, you can add a specific Object type into it.
<? extends Super> don't means, you can add any sub type of Super. It means, You can assign any sub type collection to it.

1) Why does line 1 successfully compile?
You declare list to be "a list of something that derives from Super". You assign a list of Sub to it. Sub is "something that derives from Super".
2) Is line 1 a good practice in declaring a List (or other collections)?
No. Wildcards are for function parameters. Local variables should be as specific as possible in their generics arguments to avoid problems such as the one you're facing.
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1?
Fallacy. list was declared to have elements of "something that derives from Super", not Sub as you claim. And you can't add a Sub to the list because the "something" might be something other than Sub; it might be Sub2, and adding would be equivalent to this illegal assignment:
class Super {}
class Sub extends Super {}
class Sub2 extends Super {}
Sub2 s = new Sub();
The key misunderstanding here seems to be that you think the wildcard somehow gets substituted on assignment. It isn't. It remains a wildcard, and only compatibility checks are done.
4) Eclipse's autocompletion says only "null" elements are allowed in list now. Why?
null is the only value that is of any possible reference type, and is therefore compatible with the list no matter what the wildcard stands for.

1) Why does line 1 successfully compile?
You basically define the list to contain elements of any type that extends (or is) Super, i.e. the compiler knows that every element in that list should at least have the properties of Super.
Since Sub is a subclass of Super and thus any list containing only Sub elements also meets the requirement of all elements being instances of Super, List<? extends Super> list = new ArrayList<Sub>(); is correct.
2) Is line 1 a good practice in declaring a List (or other collections)?
As a local variable that depends on personal style, IMHO. When declaring parameters that way (or instance/static variables) that's often not only good style but also needed.
Consider a method that iterates over a collection of numbers and returns a sum.
You could declare the parameter to be Collection<Number> but then you couldn't pass a Collection<Integer> without a nasty cast. If the parameter is declared as Collection<? extends Number> you can pass Collection<Integer>.
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1?
The reason is that the compiler doesn't know the exact type of the elements in the list. Is it a list of Super or a list of Sub?
As an example take List<? extends Number> list. You don't know whether you have a List<Number>, a List<Double> or a List<Integer> and thus can't tell whether list.add( new Integer(1) ); would be ok or not. That's how the compiler sees it.
4) Eclipse's autocompletion says only "null" elements are allowed in list now. Why?
I'd have to guess here but adding null to a list would be ok since no matter what type the actual list declares, you can always cast null to that type.

Related

super keyword in generics [duplicate]

I went through these topics
Generics..? Super T
Bounding generics with 'super' keyword
However, I still seem to be kind of lost with super keyword:
When we declare a collection like that:
List<? super Number> list = null;
list.add(new Integer(0)); // this compiles
list.add(new Object()); // this doesn't compile
shouldn't it be the opposite - we have a list that contains some objects (of unknown type) which are parents of Number. So Object should fit (since it is the parent of Number), and Integer shouldn't. The opposite is the case for some reason.
Provided we have the following code
static void test(List<? super Number> param) {
param.add(new Integer(2));
}
public static void main(String[] args) {
List<String> sList = new ArrayList<String>();
test(sList); // will never compile, however...
}
It is impossible to compile the above code (and my sanity suggests that this is the right behaviour), but the basic logic could prove the opposite:
String is Object, Object is superclass of Number. So String should work.
I know this is crazy but isn't this the reason why they didn't allow <S super T> constructs? If yes, then why <? super T> is allowed?
Could someone help me restore the missing part of this logic chain?
The bounded wildcard in List<? super Number> can capture Number and any of its supertypes. Since Number extends Object implements Serializable, this means that the only types that are currently capture-convertible by List<? super Number> are:
List<Number>
List<Object>
List<Serializable>
Note that you can add(Integer.valueOf(0)) to any of the above types. however, you CAN'T add(new Object()) to a List<Number> or a List<Serializable>, since that violates the generic type safety rule.
Hence it is NOT true that you can add any supertype of Number to a List<? super Number>; that's simply not how bounded wildcard and capture conversion work. You don't declare a List<? super Number> because you may want to add an Object to it (you can't!); you do because you want to add Number objects to it (i.e. it's a "consumer" of Number), and simply a List<Number> is too restrictive.
References
Angelika Langer's Generics FAQs
What is a bounded wildcard?
When would I use a wildcard parameterized type with a lower bound? ("When a concrete parameterized type would be too restrictive.")
Why is there no lower bound for type parameters? ("Because it does not make sense.")
JLS 5.1.10 Capture Conversion
See also
Effective Java 2nd Edition, Item 28: Use bounded wildcards to increase API flexibility
"PECS stands for producer-extends, consumer-super
Related questions
Too many to list, PECS, new Integer(0) vs valueOf, etc
For the first part List<Number> fits in List<? super Number> but you can't add an Object to a List<Number>. That's why you can't add an Object to List<? super Number>.
On the other hand you can add every subclass of Number (Number included) to your list.
For the second part, String is an Object, but String isn't a superclass of Number.
If it worked like this, as every class is a subclass of Object, super would have no meaning.
Let's see every possible cases with List<? super Number> :
The passed list is a List<Object>
List<Object> will work
Object fits in <? super Number>
You can add any subtype of Number to a List<Object>
Even if you could also add String in it the only thing you're sure of is that you can add any subclass of Number.
The passed list is a List<Number> :
List<Number> will work
Number fits in <? super Number>
You can add any subtype of Number to a List<Number>
The passed list is a List<Integer> (or any subclass of Number):
List<Integer> won't work
Integer is a subclass of Number so it is exactly what we want to avoid
Even if an Integer fits in a Number you wouldn't be abble to add any subclass of Number in a List<Integer> (for example a Float)
super doesn't mean a subclass.
The passed list is a List<String> (or any class not extending Number nor in the "super hierarchy" of Number (ie. Number and Object) :
List<String> won't work
String doesn't fit in Number "super hierarchy"
Even if String fits in Object (which is a super class of Number) you woudln't be sure to be able to add a Number to a List that contain any subclass from one of the super classes of Number)
super doesn't mean any subclass of one of the super classes, it only means one of the super classes.
How does it work ?
You could say that as long as you can add any subclass of Number with your typed List, it respects the super keyword.
I didn't get it for a while. Many of the answers here, and the other questions show specifically when and where certain usages are errors, but not so much why.
This is how I finally got it. If I have a function that adds Numbers to a List, I might want to add them of type MySuperEfficientNumber which is my own custom class that implements Number (but is not a subclass of Integer). Now the caller might not know anything about MySuperEfficientNumber, but as long as they know to treat the elements added to the list as nothing more specific than Number, they'll be fine.
If I declared my method as:
public static void addNumbersToList(List<? extends Number> numbers)
Then the caller could pass in a List<Integer>. If my method added a MySuperEfficientNumber to the end of numbers, then the caller would no longer have a List of Integers and the following code wouldn't work:
List<Integer> numbers = new ArrayList<Integer>();
addNumbersToList(numbers);
// The following would return a MySuperEfficientNumber not an Integer
Integer i = numbers.get(numbers.size()-1)
Obviously this can't work. And the error would be inside the addNumbersToList method. You'd get something like:
The method add... is not applicable for the arguments (MySuperEfficientNumber)
Because numbers could be any specific kind of Number, not necessarily something that MySuperEfficientNumber is compatible with. If I flipped the declaration around to use super, the method would compile without error, but the caller's code would fail with:
The method addNumbersToList(List<? super Number>)... is not applicable for the arguments (List<Integer>)
Because my method is saying, "Don't think that your List can be of anything more specific than Number. I might add all sorts of weird Numbers to the list, you'll just have to deal with it. If you want to think of them as something even more general than Number -- like Object -- that's fine, I guarantee they'll be at least Numbers, but you can treat them more generally if you want."
Whereas extends is saying, "I don't really care what kind of List you give me, as long as each element is at least a Number. It can be any kind of Number, even your own weird, custom, made-up Numbers. As long as they implement that interface, we're good. I'm not going to be adding anything to your list since I don't know what actual concrete type you're using there."
List<? super Number> means that the reference type of the variable suggests we have a list of Numbers, Objects or Serializables.
The reason you can't add an Object, is because the compiler does not know WHICH of these classes are in the generic definition of the actual instantiated object, so it only allows you to pass Number or subtypes of Number, like Double, Integer and so on.
Let's say we have a method that returns a List<? super Number>. The creation of the object inside the method is encapsulated from our view, we just can't say if it is something like this:
List<? super Number> returnValue = new LinkedList<Object>();
or
List<? super Number> returnValue = new ArrayList<Number>();
So, the generic type could be Object or Number. In both cases, we would be allowed to add Number, but only in one case we would be allowed to add Object.
You have to distinguish between the reference type and the actual object type in this situation.
There are two angles here: what you can put into a collection and what you can get from a collection, when bounded types are involved.
Let's look at the ? extends Number case first. When a collection with such bounds is defined, what we know is that : every element will have an upper bound as Number. We don't know the exact type (might be an Integer/Long/etc), but we do know, for sure, that its upper bound is Number.
So reading from such a collection gets us a Number. This is the only guaranteed type we can get from it.
Writing to such a collection is prohibited. But why? Didn't I say that while we read - we will always get a Number, so why prohibit writing to it? The situation is slightly more involved here:
List<Integer> ints = ....;
List<? extends Number> numbers = ints;
numbers.add(12D); // add a double in here
If addition would have been allowed into numbers, you could have effectively added a Double in a List of Integers.
Now to your example:
List<? super Number> list = null;
list.add(new Integer(0));
list.add(new Object());
We know about list that it contains a certain supertype of Number, for example Object.
Reading from such a list would get us a certain type X, where X would be a parent of Number. So what would that be? You can't really know. It could be a theoretical MyNumber extends Number, or much simpler: an Object. Since you can't know for sure, the only safe thing to read from that would be the super-type of everything - Object.
What is a bit weird may be :
List<? super String> list = ...;
String s = list.get(0); // fails, compiler does not care that String is final
Writing to it is slightly more complicated, but only slightly. Remember what we know is inside that list: it's a type that Number extends/implements (if it were an interface), so you can always assign a subtype (or Number itself) to that supertype.
Some type X
/ \
|
Number
/ \
|
Some type Y that we an put in here
List<? super Number> is such a List<AncestorOfNumber> where we can implicitely cast each Number to its super type AncestorOfNumber.
Consider this: What generic type needs to be ???? in the following example?
InputStream mystream = ...;
void addTo(List<????> lsb) {
lsb.add(new BufferedInputStream(mystream));
}
List<BufferedInputStream> lb = new ArrayList<>();
List<InputStream> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
...
{ addTo(lb); addTo(li); addTo(lo); }
The answer: ???? is anything to which we can cast BufferedInputStream, which is that very same or one of its ancestors: ? super BufferedInputStream
May I give a very simple Example.
public void add(List<? super Number> list) {
}
this will allow these calls
add(new LinkedList<Number>());
and everything above Number like
add(new LinkedList<Object>());
but nothing below the hierarchy so not
add(new LinkedList<Double>());
or
add(new LinkedList<Integer>());
So since its not clear for the program to know whether you give a List with Number or Object the compiler cannot allow you to add anything above Number to it.
For example a List would not accept an Object in spite of Object who would accept a Number. But since this is not clear the only valid input would be Number and its sub types.

Are we allowed to use wildcard during instantiation

why are these declaration invalid in Java?
List<Number> test = new ArrayList<? extends Number>();
List test = new ArrayList<? extends Number>();
are we not allowed to use wildcard during instantiation. and if the wildcards are only useful for passing them to methods?
and List<Object> test = new ArrayList<Integer>(); is illegal because generics are not covariant correct?
The ? wildcard character means "unknown" not "any". It doesn't make any sense to instantiate a new container of unknown contents, what would you put in there? It can't really be used for anything!
So the declaraion new ArrayList<? extends Number>() Means "some specific thing that extends number, but I don't know what." It does not mean "anything that extends number."
The List<Number> you assigned it to would allow both Double and Integer to be added to it, but the actual contents of a List<? extends Number> might be Float! (or whatever else.)
Consider what would happen in this code if the wildcard worked as an "Any":
List<Integer> listInteger = new ArrayList<Integer>();
listInteger.add(Integer.valueOf(1));
List<? extends Number> listWildCard = listInteger;
listWildCard.add(Double.valueOf(1.0)); //This does not compile
Integer integer = listInteger.get(1);//because this would throw a ClassCastException
Footnote regarding your second example:
Declaring a paramaterized type with no type parameter is called using the Raw Type. This is considered a programming error. The syntax is only legal so that code written before java 5 still compiles. Just don't do it if your scenario isn't backward compatability with pre-java 5.
To understand why it is not allowed to create objects of wildcard parameterized types, you must first understand what's the use of wildcard parameterized types.
Why wildcards?
As you already know that Java generics are invariant. So a List<Number> is not a super class of List<Integer>, even though their type arguments are covariant. So, what if you want such a behaviour in generics too, like having the same reference pointing to different objects? That polymorphic thing, as you would name it. What if you want a single List reference to refer to list of Integer, Float, or Double?
Wildcards to the rescue:
With wildcards, you can achieve the above mentioned behaviour. So, a List<? extends Number> can refer to a List<Integer>, List<Double>, etc. So, the following declarations are valid:
List<? extends Number> numbers = new ArrayList<Integer>();
numbers = new ArrayList<Double>();
numbers = new ArrayList<Float>();
numbers = new ArrayList<String>(); // This is still not valid (you know why)
So what did we change here? Just the reference type of the numbers. Note that generics were introduced in Java for enforcing stronger compile time check. So, it's primarily the compiler's job to decide whether the declaration of a parameterized type is conforming to the rule or not. Without wildcard, compiler shows you error for a List<Number> refering to List<Integer>.
So, wildcards are just a way to introduce co-variance like behaviour into generics. By using wildcards, you increase the flexibility, or you can say, reduce the restriction that compiler enforces. A List<? extends Number> reference tells the compiler that the list can refer to a list of Number or any subtype of Number(Of course, there are lower bounded wildcards too. But that's not the point here. The differences between them is already discussed in many other answers on SO only).
Major uses of wildcard parameterized type you would see with method parameters, where you want to pass different instantiation of a generic type for a single method parameter:
// Compiler sees that this method can take List of any subtype of Number
public void print(List<? extends Number> numbers) {
// print numbers
}
But at runtime, for creating an object you have to give a concrete type. A wildcard - bounded, or unbounded, is not a concrete type. ? extends Number could mean anything that is subtype of Number. So what type of List would you expect to be created when you create a List<? extends Number>?
You can consider this case similar to the reason why you can't instantiate an interface. Because they aren't just concrete.
There is a workaround. Really?
Although this is illegal, you would be surprised to know that there is a workaround, as explained in - Java Generics FAQs. But I really don't think you would ever need that.
When you instantiate a parameterized class, the parameter has to be some known, concrete type. It can be parameterized class even with ? but for inference reasons it has to be concrete. E.g. this is a valid declaration: new ArrayList<List<?>>();
The trick here is that the methods that use the type parameter in the arguments of their signature require the type of the argument to be lower bound. That is, any parameter that you pass in can be cast to the parameter type. Example:
public void fillUp(List<? super T> param)
The fillUp method takes a collection and fills it with T type objects. The param list must be able to handle the T objects so it is declared that the list can contain types that are ancestors of T, T can be safely cast to that type. If T was not a concrete type, like ? extends Number, then it would be impossible to exactly define all ancestors of T.
That's not a valid declaration as it's not a known type. You're not specifying a full type here. new ArrayList<Number> can accept anything that extends Number by subtyping so your use of ? extends Foo is not a valid need.
List<Number> can accept Integer, Long, etc. There's no way to do the equivalent of ? super Foo as it would be semantically meaningless beyond List or List<Object> with a strange artificial restriction.
Your current definition is not true, The generic type should be same in both sides or should be have inheritance relation.
Java generics are not covariant. See, for example, the article Java theory and practice: Generics gotchas by Brian Goetz. Your first example has two problems. First, when you instantiate a type it must be fully specified (including any type parameters). Second, the type parameters must exactly match the left side.
Regarding type covariance (or lack thereof), this is also not legal:
List<Number> test = new ArrayList<Integer>();
despite the fact that Integer extends Number. This also explains why the second example is illegal. A raw type is more or less the same as binding the type parameter to Object, so it would be like:
List<Object> test = new ArrayList<Integer>();
which again fails because generics are not covariant.
As to why the type parameters must be fully specified, the Java Language Specification, §8.1.2 explains the concept:
A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments.
You can only instantiate an actual type. As long as a type parameter of a generic type is unbound, the generic type itself is incomplete. You need to tell the compiler which specific parameterized type (among the set defined by the generic class) is being instantiated.
As to why generics are not covariant, this was intended to prevent the following sorts of errors:
List<Integer> iTest = new ArrayList<Integer>();
List<Number> test = iTest;
test.add(Double.valueOf(2.5));
Integer foo = iTest.get(0); // Oops!
Do not get confused by the Java inheritance concept and wildcard (e.g. ? here) syntex of Java generic concept. Both are not the same and none of the inheritance rule applies to java generic concept.
Hence Number is not same as ? extends Number
Please note that Java Generic wildcard is intended to tell the compiler about the intended object use. At runtime, it does not exist at all!
If you see generic in java just as a tool to prevent you to make mistakes, you should not go wrong in understanding this concept.

Java Generics (bounded wildcards)

According to the book "Effective Java" of Joshua Bloch there is a rule about how/when use the bounded wildcards in generics. This rule is PECS (Producer-Extends, Comsumer-Super). When I study the following example:
Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);
I understand that this rule fits perfect in this example. I have to declare the method pushAll as the following sample:
// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
{
push(e);
}
}
But what happens if I have the following example?
Stack<Integer> integerStack = new Stack<Integer>();
Iterable<Number> numbers = ... ;
integerStack.pushAll(numbers);
I have to declare the pushAll as it follows:
public void pushAll(Iterable<? super E> src) {
for (E e : src)
{
push(e);
}
}
According to PECS rule the above declaration is wrong. But I want to have a Stack of Integers and pass to this Stack a Number. Why not to do it?
Why should I always use the extends keyword? Why using super is wrong?
Of course the same stands for the comsumer's point of view. Why a consumer should always be super?
PS: To be more specific you can find this above example at the sector "Item 28" of the referred book.
When you declare a Stack<Foo> you mean a Stack of Foos, or subclasses of Foo. As an example, you would expect to be able to put a String in a Stack<Object>. The other way is not true, you should not be able to insert another Object, in a Stack<String>.
In your example you declare a Stack<Integer>. You should be able to put Integers in this stack, but not other Numbers (like a Double), which you would if you declared the parameter <? super E>. That's why the put-method should have a paramter of the type <? extends E>.
Trying to store arbitrary numbers in a Stack can't possibly work, since a Number could be something other that an Integer. So your example doesn't make much sense.
You would use super when the object asts as a consumer, i.e. when instances of the generic type of the object are passed as arguments to methods of the object. For example:
Collections.sort(List<T>, Comparator<? super T>)
In this example, the sort method takes T instances from the collection, and passes them as argument to the compare(T o1, T o2) of the comparator.
Contrast this to your first example, where the Iterable src is a producer. The pushAll() method calls a method of the Iterable which roduces (i.e. returns) instances of T. In this case, the iterable is a producer, hence the use of ? extends T
In the pushAll method, you are not passing type E, but any type that extends E. So, instead of passing an Iterable of Numbers, you can pass any Iterable of a type that extends Number.
The original example uses a Number type because you can then pass any type that is a subclass of Number, like Integer, BigDecimal and so on.
In your example, you are doing it the other way around. You are using Integer to declare your Stack. Therefore, pushAll will only be able to accept those classes that are extended by Integer. You will not be able to use Numbers (or any other class, because Integer is a final class).
First thing to notice is that Integer extends Number, so you shouldn't be pushing Number objects into a Stack of Integers. However, the first sample will work with Integers, Floats, BigDecimal and all other Number subclasses.
Your example doesn't make much sense. A construct like <? extends Number> means that Number and every type is allowed which inheits from Number. So you define an upper and a lower boundary, from type Number down to the most specific one. The other way round, <? super Number> means that Number and any of its supertyes are allowed. Since Number extends Object and implements Serializable the following three types are allowed:
java.lang.Number
java.lang.Object
java.io.Serializable
In your example you declare the generic type Stack<Integer>. Let's consider the following.
Your Stack is never be able to hold items of any super type of Integer
Your Stack is never be able to hold items of any subtype of Integer, since Integer class is final and thus it can't be subclassed.
So, if you want to declare the generic type Stack<Integer>, your iterable is of type Iterable<Integer> and thus your Stack can only hold items of type Integer. You are totally right with the mnemonic PECS, but this only works if you have choosen a concrete type which has at least one super type and at least one subtype.

Is the unbounded wildcard type used only to create references?

I have seen a lot of cases where in, the ? type is used only to create references. And we are not able to add any Objects other than null. So, is the use of '?', only to create references?
Also, what is the need / use of giving the option to add a null? They could simply have implemented with no add() method at all, right? Any reason for giving the option of adding null into List < ? > ?
List is a generic interface, and the same interface regardless of the generic type. As such, all implementors are required to redefine all methods in it. As far as being able to add null into an "unbounded" list, it's not as much a matter of methods as much as a requisiste of respecting the interface:
the unbound generic wildcard allows a reference to a List (or other generic class) to be passed around to methods without requiring a compile-time knowledge of the specific type it will contain. However, the contract of the generic must still be respected. This poses no big problem for read operations, since whatever is in the List, it will always (also) be a java.lang.Object. On the other hand, insert operations are almost impossible, since lacking informations about the accepted types, no type can be accepted - e.g. a List can only accept strings, but an Object could be anything else - a Number for example, and thus cannot be accepted safely.
Null works safely since (assuming the List accept null values), it will be the only value that's always valid to be put inside the list (a null can be cast to any valid type).
Edit: just since i noticed i didn't actually answer your question: yes, the <?> wildcard (and the extended forms as well <? extends X> and <? super X> are only valid when defining references, and cannot be used in generic instantiation.
Firstly, null is a member of all types. That's part of the language specification that pre-dates generics, so you can' do anything about that.
As for the use of ?, you can't * instantiate* a wildcard-typed generic class, that you can't do this:
List<? extends Number> list = new ArrayList<? extends Number>(); // error
because it does't make sense. The wildcard type is used to tell the compiler "I don't know what the type of the list will be, but it will be bounded by Number". However, when the code executes, a List with a type bounded by Number will be assigned to the variable.
Wildcards are most often seen as parameters:
public static void doSeomthing(List<? extends Number> list) {
// use the list knowing only that the elements are a subclass of Number
// but knowing which exact class they are
}

Class<? extends SomeSuperclass> is apparently not allowed. What makes sense instead?

This is an Android app, but presumably it happens the same in Java. I have a type LevelFactory from which I derive Level1Factory, Level2Factory, etc. I want to have an array of these classes so I can instantiate a level given the array index.
I can just have a Class[] and put them in that and then just cast them to LevelFactory when I need to use them, but I was wondering what the proper thing to do is.
This is obviously an error "Incompatible types":
new Class<LevelFactory>[] {Level1Factory.class,Level2Factory.class};
However, I was surprised to see that this is also an error "Generic array creation":
new Class<? extends LevelFactory>[] {Level1Factory.class,Level2Factory.class};
The following works, but it gives the "Unchecked assignment" warning when assigned to a variable:
new Class[] {Level1Factory.class,Level2Factory.class};
The last is the only option I can get to work. I just ignore the warning, but I would like to do it using generics if that's actually possible.
I would recommend you to read Item 25 "Prefer lists to arrays" of book "Effective Java". There Joshua Bloch writes:
Why is it illegal to create a generic array? Because it isn’t typesafe. If it were
legal, casts generated by the compiler in an otherwise correct program could fail at
runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system.
UPD: Maybe with concrete example it would be more understandable.
First of all arrays are covariant which means that SuperClass[] can be cast to SubClass[] and vice versa. It also means that it's legal to cast AnyConcreteClass[] to, say, Object[].
So Let's assume that it's possible to have Set<Cat>[] (but it is NOT). If somebody cast this array to Object[] and then add there a set of Dog instances, Java couldn't guarantee anymore that our array contains only sets of Cat instances. Breaking type safety it breaks essence of generics. That is why it's illegal have generic array.
Set<Cat>[] cats = new Set<Cat>[]; // illegal
Object[] objects = cats;
objects[1] = new Set<Dog>();
cats[1].add(new Cat()); // Oops! TypeCastException
Honestly saying this example also was taken from Effective Java :)
Two questions:
Do you really need an Array? Arrays don't work great with generics. So an ArrayList<LevelFactory> might be the better solution
Do you really need the downcast to the special type (Level1Factory, Level2Factory)? If they have a common super method which is defined in LevelFactory (lets say Level getLevel()) you should not need to downcast them. Just call getLevel() and you get the correct Level instance from your factory.
Another note because this seems to be a common pitfall:
new Class<? extends LevelFactory>
This is not a valid statement (it does not matter if its an array or not). <? extends T> is only valid for the type on the left side. It defines that the generic type of the created Object can be T or derived from T. For Collections this does not mean that they can store objects of T or derived from T (which can a Collections of T anyway).
List<LevelFactory> list = new ArrayList<LevelFactory>() This means you can add objects of LevelFactory, Level1Factory and Level2Factory to list. When you want to receive objects from list they are of type LevelFactory.
List<? extends LevelFactory> list = new ArrayList<LevelFactory>() Means you can receive objects of LevelFactory from list. BUT you cannot add any object to list in a typesafe way because you don't know the exact generic type of list. That because you can also assign new ArrayList<Level1Factory>() to list. Which means that you can't even add LevelFactory objects to list because they don't implement Level1Factory.
In general <? extends Something> on collections is not what you want in most cases.
You can't create array this way new SomeClass<Type>[10] but only this way new SomeClass[10]. Consider using ArrayList<SomeClass<Type>> instead.

Categories

Resources