java generics bounds - java

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.

Related

How to safely cast generic wildcard "?" to known type parameter in Java?

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!).

Why is this generic assignment illegal?

I have a class:
class Generic<T> {
List<List<T>> getList() {
return null;
}
}
When I declare a Generic with wildcard and call getList method, the following assignment is illegal.
Generic<? extends Number> tt = null;
List<List<? extends Number>> list = tt.getList(); // this line gives compile error
This seems odd to me because according to the declaration of Generic, it's natural to create a Generic<T> and get a List<List<T>> when call getList.
In fact, it require me to write assignment like this:
List<? extends List<? extends Number>> list = tt.getList(); // this one is correct
I want to know why the first one is illegal and why the second one is legal.
The example I give is just some sample code to illustrate the problem, you don't have to care about their meaning.
The error message:
Incompatable types:
required : List<java.util.List<? extends java.lang.Number>>
found: List<java.util.List<capture<? extends java.lang.Number>>>
This is a tricky but interesting thing about wildcard types that you have run into! It is tricky but really logical when you understand it.
The error has to do with the fact that the wildcard ? extends Number does not refer to one single concrete type, but to some unknown type. Thus two occurrences of ? extend Number don't necessarily refer to the same type, so the compiler can't allow the assignment.
Detailed explanation
The right-hand-side in the assignment, tt.getList(), does not get the type List<List<? extends Number>>. Instead each use of it is assigned by the compiler a unique generated capture type, for exampled called List<List<capture#1 extends Number>>.
The capture type List<capture#1 extends Number> is a subtype of List<? extends Number>, but it is not type same type! (This is to avoid mixing different unknown types together.)
The type of the left-hand-side in the assignment is List<List<? extends Number>>. This type does not allow subtypes of List<? extends Number> to be the element type of the outer list, thus the return type of getList can't be used as the element type.
The type List<? extends List<? extends Number>> on the other hand does allow subtypes of List<? extends Number> as the element type of the outer list. So that is the right fix for the problem.
Motivation
The following example code demonstrates why the assignment is illegal. Through a sequence of steps we end up with a List<Integer> which actually contains Floats!
class Generic<T> {
private List<List<T>> list = new ArrayList<>();
public List<List<T>> getList() {
return list;
}
}
// Start with a concrete type, which will get corrupted later on
Generic<Integer> genInt = new Generic<>();
// Add a List<Integer> to genInt.list. This is not necessary for the
// main example but migh make things a little clearer.
List<Integer> ints = List.of(1);
genInt.getList().add(ints);
// Assign to a wildcard type as in the question
Generic<? extends Number> genWild = genInt;
// The illegal assignment. This doesn't compile normally, but we force it
// using an unchecked cast to see what would happen IF it did compile.
List<List<? extends Number>> list =
(List<List<? extends Number>>) (Object) genWild.getList();
// This is the crucial step:
// It is legal to add a List<Float> to List<List<? extends Number>>.
// list refers to genInt.list, which has type List<List<Integer>>.
// Heap pollution occurs!
List<Float> floats = List.of(1.0f);
list.add(floats);
// notInts in reality is the same list as floats!
List<Integer> notInts = genInt.getList().get(1);
// This statement reads a Float from a List<Integer>. A ClassCastException
// is thrown. The compiler must not allow us to end up here without any
// previous type errors or unchecked cast warnings.
Integer i = notInts.get(0);
The fix that you discovered was to use the following type for list:
List<? extends List<? extends Number>> list = tt.getList();
This new type shifts the type error from the assignment of list to the call to list.add(...).
The above illustrates the whole point of wildcard types: To keep track of where it is safe to read and write values without mixing up types and getting unexpected ClassCastExceptions.
General rule of thumb
There is a general rule of thumb for situations like this, when you have nested type arguments with wildcards:
If the inner types have wildcards in them, then the outer types often need wildcards also.
Otherwise the inner wildcard can't "take effect", in the way you have seen.
References
The Java Tutorial contains some information about capture types.
This question has answers with general information about wildcards:
What is PECS (Producer Extends Consumer Super)?

Misunderstanding on Contravariance in Java with code example [duplicate]

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.

Java Generic ? extends String

While I was going through some generics question I came across this example. Will you please explain why list.add("foo") and list = new ArrayList<Object>() contain compailation issues?
In my understanding List of ? extends String means "List of Something which extends String", but String is final ? can only be String. In list.add() we are adding "foo" which is a string. Then why this compilation issue?
public class Generics {
public static void main(String[] args) {
}
public static void takelist(List<? extends String> list){
list.add("foo"); //-- > error
/*
* The method add(capture#1-of ? extends String) in the
* type List<capture#1-of ? extends String> is not applicable
* for the arguments (String)
*/
list = new ArrayList<Object>();
/*
* Type mismatch: cannot convert from ArrayList<Object> to List<? extends String>
*/
list = new ArrayList<String>();
Object o = list;
}
}
For starters, the java.lang.String class is final, meaning nothing can extend it. So there is no class which could satisfy the generic requirement ? extends String.
I believe this problem will cause all of the compiler errors/warnings which you are seeing.
list.add("foo"); // String "foo" does not extend String
list = new ArrayList<Object>(); // list cannot hold Object which does not extend String
It is true what you say. String is final. And so you can reason that List<? extends String> can only be list of string.
But the compiler isn't going to make that kind of analysis. That is to say, the compiler will not assess the final or non-final nature of String (see comments). The compiler will only let you put null into your list.
You can pull stuff out though.
String s = list.get(0);
While String is final, this information is not used.
And in fact, with Java 9, it may no longer be final (rumor has it that Java 9 may finally get different more efficient String types).
Without knowing it is final, List<? extends String> could be e.g. a List<EmptyString> of strings that must be empty.
void appendTo(List<? extends String> l) {
l.append("nonempty");
}
appendTo(new ArrayList<EmptyStrings>());
would yield a violation of the generic type.
As a rule of thumb always use:
? extends Type for input collections (get is safe)
? super Type for output collections (put is safe)
Type (or maybe a <T>) for input and output collections (get and put are safe, but the least permissive).
I.e. this is fine:
void appendTo(List<? super String> l) {
l.append("nonempty");
}
appendTo(new ArrayList<Object>());
You have already mentioned that String is a final type and therefore there is no point in repeating this fact. The point that is important to note is that none of the following Lists allows adding an element:
List<?> which is a List of anything.
List<? extends SomeType> which is a List of anything that extends SomeType.
Let's understand it with an example.
The List<? extends Number> could be List<Number> or List<Integer> or List<Double> etc. or even a List of some other type that hasn't been defined yet. Since you can not add any type of Number to a List<Integer> or any type of Number to a List<Double> etc., Java does not allow it.
Just for the sake of completeness, let's talk about List<? super Integer> which is List of anything that is a super/parent type of Integer. Will the following compile?
Object obj = 10.5;
list.add(obj);
As you can guess, of course NOT.
What about the following?
Object obj = 10.5;
list.add((Integer) obj);
Again, as you can guess, indeed it will compile but it will throw ClassCastException at runtime. The question is: why did not Java stop us in the first place by failing the compilation itself? The answer is Trust. When you cast something, the compiler trusts that you already understand the cast.
So, the following compiles and runs successfully:
Object obj = 10;
list.add((Integer) obj);
list.add(20);

Why are generics said to be invariant when "? extends Klass" is allowed?

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);

Categories