In Java, its said that:
String[] is subtype of Object[]
so arrays are said to be covariant. But for generics they say:
List<X> will not be subType of List<Y>.
and hence its invariant. But the question is, "are generics really invariant"?
For example, if I give:
List<? extends Exception>
this means that the list can take the subtype of Exception, say for example this is valid:
List<? extends Exception> k = new ArrayList<NumberFormatException>();
Then why Generics are said to be invariant?
List<? extends Exception> k = new ArrayList<NumberFormatException>();
this means that the list can take the subtype of Exception
Not quite. You can assign to k a List -- or any of its subtype, as you have ArrayList -- in this case, of any subtype of Exception.
But you cannot add to k any subtype of Exception, or anything for that matter, because k is a List of some unknown subtype of Exception. For example,
k.add(new NumberFormatException());
would give an error.
Retrieval is also restricted to the known type:
NumberFormatException e1 = k.get(0); // error
Exception e2 = k.get(0); // ok, anything in k must be an Exception
NumberFormatException e3 = (NumberFormatException) k.get(0); // ok, but the usual downcast issues exist
I think the simple answer to your question is a semantic one.
List<Object> is not a supertype for List<String>. Collection<String> is its supertype, while ArrayList<String> is one of its possible subtypes.
Putting it in another way :
Object[] array = new String[2]; //is a valid declaration.
List<Object> list = new ArrayList<String>(); //is not.
Arrays are covariant in java, but they should not be. This is just one of many design flaws in java language in general and typing system in particular.
Consider this code:
public void messUp(Object objects[]) { objects[0] = "foo"; }
Integer ints[] = new Integer[] {1,2,3};
messUp(ints);
This compiles without warning, but throws ArrayStoreException when executed.
To answer your question, List<T> is invariant, because List<String> is not a subclass of List<Object>. The "extends" keyword is used to constrain the type parameter, but doest not affect the variance: <T> void foo(List<T>) means that you can pass a list of elements of any type to foo, <T extends Exception> void foo(List<T>) means the same thing, except it constrains the type parameter T, such that it must be a subclass of Exception.
This does not make List<T> a subclass of List<Exception>, they are still two different classes. If it was a subclass, you could do the same trick with it that I did with arrays above:
<T extends Exception> void foo(List<T> exceptions) {
List<Exception> l = exceptions;
l.add(new RuntimeException());
}
But this will not compile, because List<T> cannot be assigned to List<Exception> (because it's not a subclass);
Related
How to safely cast Class<?> (returned by Class.forName()) to Class<Annotation> without issuing "Unchecked cast" warning?
private static Class<? extends Annotation> getAnnotation() throws ClassNotFoundException {
final Class<?> loadedClass = Class.forName("java.lang.annotation.Retention");
if (!Annotation.class.isAssignableFrom(loadedClass)) {
throw new IllegalStateException("#Retention is expected to be an annotation.");
}
#SuppressWarnings("unchecked")
final Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) loadedClass;
return annotationClass;
}
Multiple misconceptions need to be explained before delving into the answer.
You're using the wrong variance
final Class<Annotation> annotationClass = (Class<Annotation>) loadedClass;
This is actually illegal in any case. Try it:
Class<Number> n = Integer.class;
That won't compile.
Generics are invariant. It means that within the <>, you can't use a supertype as a standin for a subtype or vice versa.
Normal java (when <> are not involved) is covariant. Any subtype is a stand-in for one of its supertypes. This:
Number n = Integer.valueOf(5);
is perfectly legal java. But in generics world it isn't. If you want it to be, then, you have to opt into it: X extends Y is how you opt into covariance, and X super Y is how you opt into contravariance (contravariance is as if Integer i = new Number(); was legal - a SUPERtype can stand in for a subtype).
This is all because that's just how the universe ends up working out. If generics were naturally covariant, this would compile:
List<Integer> listOfInts = new ArrayList<>();
List<Number> listOfNums = listOfInts;
listOfNums.add(Double.valueOf(1.0));
int i = listOfInts.get(0);
but, follow along with your own eyes and you realize that code is a walking type violation. It shoves a non-integer into a list of integers. That's why opting into covariance or contravariances closes doors. If you opt into covariance, the add method is disabled *1:
List<? extends Number> list = new ArrayList<Integer>(); //legal
list.add(Integer.valueOf(5)); // will not compile
similarly, if you opt into contravariance, add works great, but get is disabled. 'disabled' in the type system sense: You can call it. But the expression list.get(i) would be of type Object:
List<? super Integer> list = new ArrayList<Number>(); // legal
list.add(Integer.valueOf(5)); // legal
Integer i = list.get(0); // won't compile
Object o = list.get(0); // this will.
With classes, where 'write' is not exactly clear, it's harder to see why Class<Annotation> c = SomeSpecificAnno.class; should fail to compile, but it does, so, that's important realization one.
Why are you using reflection here?
You can make class literals in java. This works great:
Class<? extends Number> c = Integer.class;
That's real java: You can stick .class at the end of any type and that will be an expression of type java.lang.Class, in fact, it's of type Class<TheExactThing>. So:
private static Class<? extends Annotation> getAnnotationType() {
return Retention.class;
}
works and compiles fantastically. I had to update the return type because as I explained above, returning the instance of j.l.Class that represents the Retention annotation for a method that is specced to return Class<Annotation> is as broken as returning an integer from a method that is specced to return a string.
The answer
If your code example is using java.lang.annotation.Retention as a stand-in, but your actual string here is a dynamic value that you do not know at compile time, the return Retention.class; option is off the table, then:
private static Class<? extends Annotation> getAnnotationType(String fqn) throws ClassNotFoundException {
return Class.forName(fqn).asSubclass(Annotation.class);
}
Again, do not use reflection unless there is no other way, and if you have the class in a string constant, generally you do not need reflection.
*1 ) You can call add, but only with a null literal; list.add(null); compiles, because null is trivially a valid value for any type. However, that's not particularly useful, of course.
Due to type erasure, generic type information is not accessible anymore at runtime. This means: Class<?> and Class<? extends Annotation> are indistinguishable at runtime - so you cannot do a runtime check to make the unchecked cast a checked one.
This means: you have to live with the warning (mind: a warning is not an error, it just means "this is problemattic, make sure you know what you're doing!).
This question already has answers here:
Generics <? super A> doesn't allow superTypes of A to be added to the list
(2 answers)
Closed 4 years ago.
I am trying out a easy to understand example about contravariance in Java and having a issue understanding.
In the below example I have List<? super CarBill> list1 . My understanding is i should be able to add an object of any superclass of CarBill. By that logic i should be able to add objects of Bill class to it too right ?
I get a compilation error.
package Generics;
import java.util.ArrayList;
import java.util.List;
public class VarianceTests {
static class Bill{
String vName;
String type;
Bill(String vName){
this.vName=vName;
}
Bill(String vName,String type){
this.vName=vName;
this.type=type;
}
}
static class CarBill extends Bill{
String name;
CarBill(String name)
{
super(name,"Car");
}
}
static class Car<T extends Bill> {
T car;
Car(T car){
this.car=car;
}
String getNameOfCar() {
return car.vName;
}
}
public static void main(String args[]) {
CarBill cBill = new CarBill("Baleno");
Bill bill=new Bill("Whatever");
Car car = new Car(bill); //cBill is valid too as Car accepts <? extends Bill>
List<? super CarBill> list1 = new ArrayList<>();
list1.add(cBill);
list1.add(bill);
}
public void acceptListOfCars(List<? extends Bill> list1) {
Bill b = list1.get(0); //Valid syntax
}
}
Your understanding is mistaken.
List<? super CarBill> means that the list can be a list of any super class of CarBill or CarBill itself. It could be List<Object>, it could be List<Bill>, it could even be List<CarBill>. Which one is it actually? We don't know.
Therefore, you can't add a Bill to a List<? super CarBill> because what if the list is actually a List<CarBill>? You can't add a Bill to a List<CarBill>.
In other words, you can only add CarBill or subclasses of CarBill into a List<? super CarBill>.
If your intention is to create a list that can store any type of Bill, you can create a List<Bill>.
This post might help as well.
Not quite.
Let's start with this code:
List<Integer> listOfInts = new ArrayList<Integer>();
List<Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
The above code won't in fact compile; the second line is an invalid assignment. Your line of thinking would say: But.. why? Number is a supertype of Integer, so, a list of integers is trivially also a list of numbers, no? but then the third line shows why this line of reasoning is incorrect. Java will NOT let you write the above code. What you CAN write is this: The same thing, but this time we tweak the second line:
List<Integer> listOfInts = new ArrayList<Integer>();
List<? extends Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
This time, you get a compiler error on the third line: You cannot add a double to this list. But, if you read from it, you'd get numbers out (not objects). This is all good: The above snippet of code should never compile no matter what we try because it tries to add doubles to a list of ints.
The point is: List<? extends Number> does not mean: "This list contains numbers, or any subtypes thereof". No; just like List x = new ArrayList() is legal java, List<Number> means 'this list contains numbers or any subtypes thereof' because any instance of any subtype of number can itself be used as a Number. List<? extends Number> means: This is a list restrained to contain only instances of some specific type, but which type is not known. What IS known, is that whatever that type is, it's either Number or some subtype thereof.
Hence, you can't add ANYTHING to a List<? extends Number>.
For super, a similar story:
List<? super CarBill> means: This is a list that is restricted to contain only instances of some specific type, but which type is not known. What IS known, is that, whatever type it is, it is either CarBill or some SUPERtype thereof.
The upside of doing this, is that you can add CarBill instances to a List<? super CarBill> variable. When you read from it, you'll get objects out.
My understanding is i should be able to add an object of any superclass of CarBill
No.
A List<? super CarBill> is not a list that will accept objects of any supertype of CarBill. It's a list that will accept objects of some particular supertype of CarBill, but which supertype it is is unknown.
You can add any object of type CarBill, because that is guaranteed be a subtype of type ?. But a supertype of CarBill is not guaranteed to be a subtype of ?.
For instance:
List<? super CarBill> myList = new ArrayList<Bill>();
Object o = "Anything";
Object is a supertype of CarBill. So if you could add any supertype of CarBill to the list, you would be able to add o to the list, which would mean you could add anything to the list.
I am reading on Generics in Java atm, and things go a little slow would love some help. I read from Oracles own database:
https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
At the bottom we can see List<Integer> is a subtype of List<? extends Number>
I also came across this stackoverflow question:
Java Generic type : difference between List <? extends Number> and List <T extends Number>
Which says in one answer: this is true:
((List<Integer>)list).add((int) s);
I have verified it, so that's ok. But I don't understand It completely.
What if the Wildcard is the Short class and I add a number higher than 2^15-1(=32767) Shouldn't it give error?
I even tried something like this and it works fine:
import java.util.*;
class CastingWildcard{
public static void main(String[] args){
List<? extends Number> list = new ArrayList<Short>();
int s=32770;
((List<Integer>)list).add((int) s);
System.out.println(list.get(0));
}
}
To sum up: Why Can I cast List<? extends Number> to List<Integer> when the wildcard could be Short, and even Byte, which also extends Number?
The cast makes the compiler ignore the fact, that the types may not be assignable.
At runtime the type parameters are unimportant, see type erasure.
The ArrayList internally stores the content in a Object[] array, which means you can add any reference type to the list object, if you "abuse" casting.
You may get a exception when you retrieve a Object though, since there's a cast hidden in the get statement.
Example:
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
List<String> list2 = (List) list;
list2.add("Hello World");
Integer i = list.get(0); // works
String s = list2.get(3); // works
s = list2.get(1); // ClassCastException
i = list.get(3); // ClassCastException
You can cast an object to anything you want, but it might fail at runtime. However since generics information isn't present during runtime, your code becomes essentially ((List)list).add(s);. At that point list will take any object, not just a Number. Generics can help you avoid casts and keep type safety during compile time, but during runtime they don't matter anymore.
I have the following code:
public <T extends SomeObject> long doSomething(T someObject){
List<? extends SomeObject> l = new LinkedList<>();
l.add(someObject);
}
this causes a compilation error - telling me that there is no suitable methods found: add(T),
why is that?
If l accept things that extends SomeObject shouldn't it accept someObject as it bounds to extend SomeObject?
List<? extends SomeObject> l
What do you mean by that? Of course it will generate an error.
Take this example :SomeObject is Fruit, you have 2 derived classes Apple and Orange
Your list what will it contain? Apples or Oranges? The compiler cannot tell. So it generates error.
If you replace List<? extends SomeObject> l with List<SomeObject> l. Then this will work because Apple and Orange are both Fruit.
I would advise you to use this statement:
List<T> l = new LinkedList<T>();
This is no less type-safe then
List<SomeObject> l = new LinkedList<SomeObject>();
and additionally gives you an opportunity to get objects of type T from the list without casting. T is already SomeObject so no casting required to call methods of SomeObject on T.
And all that with less typing!
Back to the problem.
First thing to note is that wildcard type "?" means unknown, this is important.
You may, however, specify an upper (? extends) or a lower (? super) constraint to it.
You declared a list as "List".
List is known to have objects of SomeObject inside. but! the exact type of objects is unknown.
Compiler can not say if there are instances of "class A extends SomeObject" or instances of "class B extends SomeObject" inside the list.
If you call list.get() it can only say that there will be an object of type SomeObject.
SomeObject obj = list.get(1); // Ok
But inserting an object of any(!) type is unsafe because the actual type of elements in the list is unknown.
You could wonder why wildcard type ever exists.
It is here to lower restriction in type casting that will be too strict otherwise.
Sample
class A { }
class A2 extends A { }
class B <T> {
void change(T a) { .. };
T read() { .. };
}
If there were no wildcards we would not be able to do this: B<A> b = new B<A2>(); - it does not work.
This is because type conversion from B<A> to B<A2> is unsafe.
Why? Let's look (copied from http://en.wikipedia.org/wiki/Generics_in_Java)
List<Integer> ints = new ArrayList<Integer>();
ints.add(2);
List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number>
nums.add(3.14);
Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
What is the solution? Sometimes, we want to do such assignments or pass parameters in a general way!
Wildcard type helps here: B<? extends A> b = new B<A2>();
Method B.void change(T a) is now disabled - this is what your question was about and explained in the first part.
Method B.T read() is still valid and returns A: A a = b.read();. Yes, it returns A2 actually but to the caller of b.read() it's visible as A.
Wildcard types are widely used in Collections Framework.
I have two classes with nested generics. Is there a way to get rid of the
Type mismatch: cannot convert from Msg<Value<String>> to Msg<Value<?>> error ?
In the last assignment
public class Value<V> {
V val;
public Value(V val) {
this.val = val;
}
#Override
public String toString() {
return "" + val;
}
}
public class Msg<T> {
T holder;
public Msg( T holder) {
this.holder = holder ;
}
public String toString() {
return "" + holder;
}
public static void main(String[] args) {
Msg<Value<String>>strMsg = new Msg(new Value<String>("abc"));
// This is OK
Msg<?>objMsg = strMsg;
// Type mismatch: cannot convert from Msg<Value<String>> to Msg<Value<?>>
Msg<Value<?>>objMsg = strMsg;
}
}
Use the following:
Msg<? extends Value<?>> someMsg = strMsg;
The problem is that the ? in Msg<Value<?>> objMsg is NOT capable of capture conversion. It's not "a Msg of Value of some type. It's "a Msg of Value of ANY type".
This also explains why along with the declaration change, I've also renamed the variable to someMsg. The Value can't just be any Object. It must belong to some type (String in this example).
A more generic example
Let's consider a more generic example of a List<List<?>>. Analogously to the original scenario, a List<List<?>> can NOT capture-convert a List<List<Integer>>.
List<List<Integer>> lolInt = null;
List<List<?>> lolAnything = lolInt; // DOES NOT COMPILE!!!
// a list of "lists of anything"
List<? extends List<?>> lolSomething = lolInt; // compiles fine!
// a list of "lists of something"
Here's another way to look at it:
Java generics is type invariant
There's a conversion from Integer to Number, but a List<Integer> is not a List<Number>
Similarly, a List<Integer> can be capture-converted by a List<?>, but a List<List<Integer>> is not a List<List<?>>
Using bounded wildcard, a List<? extends Number> can capture-convert a List<Integer>
Similarly, a List<? extends List<?>> can capture-convert a List<List<Integer>>
The fact that some ? can capture and others can't also explains the following snippet:
List<List<?>> lolAnything = new ArrayList<List<?>>(); // compiles fine!
List<?> listSomething = new ArrayList<?>(); // DOES NOT COMPILE!!!
// cannot instantiate wildcard type with new!
Related questions
Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
Very long and detailed exploration into this problem
Java Generic List<List<? extends Number>>
Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
What is the difference between <E extends Number> and <Number>?
See also
Java Generics Tutorial
Generics and Subtyping | Wildcards | More Fun with Wildcards
Angelika Langer's Java Generics FAQ
What is a bounded wildcard?
Which super-subtype relationships exist among instantiations of generic types?
My answer is similar to another, but hopefully is more clear.
List<List<?>> is a list of (lists of anything).
List<List<String>> is a list of (lists of strings).
The latter cannot be converted to the former because doing so would allow you to add a List<Number> to your List<List<String>>, which would clearly be broken.
Note that the rules for this don't change if you replace List with some type that doesn't have .add. Java would need declaration-site covariance and/or contravariance for that (like C#'s IEnumerable<out T> or Scala's List[+A]). Java only has use-site covariance and contravariance (? extends X, ? super X).
Although your generic type parameter contains a wildcard, it is not itself a wildcard. When assigning to a variable (Msg<T>) with a non-wildcard generic type T, the object being assigned must have exactly T as its generic type (including all generic type parameters of T, wildcard and non-wildcard). In your case T is Value<String>, which is not the same type as Value<?>.
What you can do, because Value<String> is assignable to Value<?>, is use the wildcard type:
Msg<? extends Value<?>> a = new Msg<Value<String>>();
Not a direct answer but i strongly recommend the reading of: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf to better understand generics.
ILMTitan has a good solution, and if you don't want to make the class specific to Value you may as well use the base type instead of generics at this point because you'll be turning off a safety feature, but there is a way. You might even be able to pass a parameter to make this method more generic, but the key is "#SuppressWarnings".
#SuppressWarnings("unchecked")
Msg<Value<?>> convert()
{
return (Msg<Value<?>>) this;
}