Trying to fully understand Generics in Java, I ran into a problem I couldn't really get around, nor find a specific solution for in the web.
I have this method:
public <T extends City> void someMethod(List<T[]> objectsList) {
List<City[]> myList = (List<City[]>) objectsList;
}
Just writing this into the compiler with provide me with an error I can ignore - however during compilation the compiler fails saying
Error:(97, 46) error: incompatible types: List<T[]> cannot be converted to List<City[]> where T is a type-variable: T extends City declared in method <T>someMethod(List<T[]>)
A simple solution is to simple run the entire List and Arrays within the received 'objectsList' and typecast them one at a time, which would work (I tried that), however I'm not really sure that's the right way to do it...
This isn't something you're going to want to do often (since generics and arrays don't mix well), but there are some important things to make note of:
T holds the type information for the method. You may not be passing in a City at all times, and in the off case you don't, Java can't help you out.
The cast is not going to be safe, due to the fact that generics are invariant; you cannot say that a List<City[]> is equivalent to a List<T[]>, even if T extends City.
With that, what you can do to fix this is use a wildcard bound on the generic types. Use them in both the declaration of what you accept to the method, and what you're storing. You then avoid the cast, and everything is type-safe.
public <T extends City> void someMethod(List<? extends T[]> objectsList) {
List<? extends T[]> myList = objectsList;
}
Because, in Java, an array is like a type. Ergo, the type City[] is completely different to T extends City I'm not sure what you're getting at, but that List<T[]> looks extremely dodgy.
How To Use Generics
Let's say you want to return an ArrayList in the reverse order to one parametrised. I don't know why, and I expect there's a perfectly good library function to do so, but let's roll with it.
public ArrayList<E> getReversedList(ArrayList<E> list) {
ArrayList<E> newList = new ArrayList(list.size());
for (int i = 0; i < list.size(); i++)
newList.add(list.size() - i, (E) list.get(i));
return newList;
}
There is no need to cast - just work with arrays of type T[]. For example, this compiles:
public <T extends City> void someMethod(List<T[]> objectsList) {
for (T[] cities : objectsList) {
for (City city : cities) {
// do something with the city object
}
}
}
The code in the method doesn't know which exact subclass of City is in the arrays, but you may assign them to a variable of type City and treat them as City objects.
The reason your cast doesn't work is due to the fact that List<SubType> is not a subtype of List<Type>.
Related
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);
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.
This question already has answers here:
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
(19 answers)
Closed 6 years ago.
Consider the snippet:
Number[] numbers = {1, 2.3, 4.5f, 6000000000000000000L};
It's perfectly okay to do the above, Number is an abstract class.
Going ahead,
List<Long> listLong = new ArrayList<Long>();
listLong.add(Long.valueOf(10));
List<Number> listNumbers = listLong; // compiler error - LINE 3
listNumbers.add(Double.valueOf(1.23));
Had Line 3 was designed to be compiled successfully,
we would end up with a List of Numbers, i.e,
for(Number num: listNumbers ){
System.out.println(num);
}
// 10
// 1.23
which are all numbers.
I came across this in a book,
Generics doesn’t support sub-typing because it will cause issues in
achieving type safety. That’s why List<T> is not considered as a
subtype of List<S> where S is the super-type of T
Which type safety would have lost in this specific case as discussed above, were the Line 3 was to be compile successfully?
List<Long> listLong = new ArrayList<Long>();
List<Number> listNumbers = listLong;
So, listNumbers and listLong would be two references to the same list, if that was possible, right?
listNumbers.add(Double.valueOf(1.23));
So, you would be able to add a Double to that list. listLong, of type List<Long>, would thus contain a Double. The type-safety would thus be broken.
If that was the case, then we could add other different subtypes of Number into listNumbers, which must be forbidden.
Imagine you're now inserting objects of type Double and Long, and later you try to use Long#reverse. Your code will compile but of course will fail at runtime (bad) the first Double it'll come through.
Let's use an example with a non-abstract base class:
public class Human {
public string getName() {
// ...
}
}
public class Student extends Human {
public void learn(Subject subject) {
// ...
}
}
public class Teacher extends Human {
public void teach(Subject subject) {
// ...
}
}
At any place where a Human is expected, a Student or Teacher will do just as well, as they fully implement the Human interface. (In this case, that getName() can be called on them.) Java inheritance guarantees that this is the case technically. Making it work semantically is the class author's job, so that his code fulfils the Liskov substitution principle.
So doesn't this mean that we can also substitute Collection<Teacher> where a Collection<Human> is expected? Not always. Consider the following method:
public class Human {
// ...
public void join(Set<Human> party) {
party.add(this);
}
}
Now, if Java allowed a Set<Student> to be passed as party, any attempts of non-Student Humans to join that party would have to fail at runtime.
As a general rule, a container of a subtype is unsuitable if the receiver (callee in case of a function argument, caller in case of a function return value) wants to put something into it, but acceptable if the receiver only want to take stuff out and use it. A container of a supertype is unsuitable if the receiver wants to take stuff out and use it, but acceptable if the receiver only ever puts stuff into it. As a result, if the receiver both takes stuff out of the collection and puts stuff into the collection, they usually must require a collection of a fixed type.
Our join method only puts Humans into the party, so we could also allow a Set<Object> or a non-generic Set or equivalently a Set<?>. Java allows us to do that with lower-bounded wildcards:
public class Human {
// ...
public void join(Set<? super Human> party) {
party.add(this);
}
}
For opening up the possibilities towards subclasses, there's upper-bounded wildcards:
public class Teacher extends Human {
public void teach(Subject subject, Set<? extends Student> schoolClass) {
for (Student student : class) {
student.learn(subject);
}
}
}
Now, if we ever subclass Student, the passed schoolClass can be a Set of that subtype, too.
The concept you are referring to is variance.
In other words, if S is a supertype of T, is List<S> a subtype, supertype, equal type, or unreleted to List<T>?
The answer for List -- and all other Java generics* -- is "unrelated", i.e. invariant.
class SuperType {}
class Type extends SuperType {}
class SubType extends Type {}
List<Type> list = ...
List<SuperType> superList = list;
superList.add(new SuperType());
// no, we shouldn't be able to add a SuperType to list
List<SubType> subList = list;
SubType item = subList.get(0);
// no, there's not necessarily only SubType items in list
*Java does have the notion of "use-site" variance, with wildcards (?). This will limit what methods are possible to call.
List<Type> list = ...
List<? super SubType> wildcardList = list;
wildcardList.add(new SubType());
// but...everything we get() is an Object
or
List<Type> list = ...
List<? extends SuperType> wildcardList = list;
SuperType item = wildcard.get(0);
// but...it's impossible to add()
FYI, some languages have the notion of definition-site variance, e.g. Scala. So List[Int] is indeed a subtype of List[Number]. That's possible with immutable collections (again, a limited set of methods), but obviously not for mutable ones.
I have written below code
class Student {}
class Student1 extends Student {}
class Student2 extends Student {}
List<? extends Student> emp = new ArrayList<>();
emp.add(new Student()); // I do not want this to happen. at compile time it should give me error
emp.add(new Student1()); // this should happen
emp.add(new Student2()); // this should happen
But in above code its not working throwing compile error in all 3 adds.
Any pointers?
No, there is no such option in Java to constrain the type parameter to just subtypes of a given type. It looks like you have taken the meaning of the syntax ? extends Type a bit too literally. Type there is the upper bound of the wildcard, and the bounds are always inclusive.
If your idea was the way it really worked, there would be no type by which you could refer to a general item of your list: the narrowest such type is Student.
In java Generics are not co-variant. So List<T> is not a sub-type of List<S> if T is a sub-type of S. This was done to ensure static, compile time type safety. If it were allowed you could do things like following:
List<Integer> intList = new ArrayList<>();
List<Number> numberList = intList;
numberList.add(1.0); // Breaks the contract that intList will contain only Integers
// (or Objects of classes derived from Integer class)
Now to allow functions which could work on Lists containing anything which extends some base class, Bounded wildcards were introduced. So for example, if you wish to write a generic add method which returns the sum of all elements in a List (irrespective of whether the list if of Type Integer, Double, Float), you can write the following code
double add(List<? extends Number> numberList) {
double ans = 0;
for (Number num : numberList) {
ans += num.doubleValue();
}
return ans;
}
The argument can be List of any Object which extends Number such as List<Double>, List<Float>, List<Short>, List<Integer>.
Now coming to your question, when you say a List is of Type <? extends Student> and add Student1() to it, the compiler is not able to verify whether the type is correct or not and it breaks the compile time safety. (Because ? is an unknown type). Normally you cannot add anything to a List having a bounded wildcard as its type (except null and except if you follow a rather complicated process). So either you can declare your list as of type Student but that will mean that your first add statement would not throw an error.
In most practical scenarios, this kind of case is handled by making Student abstract and implementing all the common functionality in the abstract class while declaring your list as of type Student.
Also if you ever want to add to a bounded wildcard type, you can write a helper function like this and call it in your method (This also accepts Student as valid type. There's no way to force a Type to extend something because type resolution is always inclusive in Java):
private <Student, S extends Student> void addd(List<Student> l, S element) {
l.add(element);
}
and call it as:
addd(emp, new Student());
addd(emp, new Student1());
addd(emp, new Student3());
Use "List<? extends Student>" when you want to access the existing elements of List.
Use "List<? super Student>" when you want to set the elements to the List.
Why can I use super only with wildcards and not with type parameters?
For example, in the Collection interface, why is the toArray method not written like this
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
super to bound a named type parameter (e.g. <S super T>) as opposed to a wildcard (e.g. <? super T>) is ILLEGAL simply because even if it's allowed, it wouldn't do what you'd hoped it would do, because since Object is the ultimate super of all reference types, and everything is an Object, in effect there is no bound.
In your specific example, since any array of reference type is an Object[] (by Java array covariance), it can therefore be used as an argument to <S super T> S[] toArray(S[] a) (if such bound is legal) at compile-time, and it wouldn't prevent ArrayStoreException at run-time.
What you're trying to propose is that given:
List<Integer> integerList;
and given this hypothetical super bound on toArray:
<S super T> S[] toArray(S[] a) // hypothetical! currently illegal in Java
the compiler should only allow the following to compile:
integerList.toArray(new Integer[0]) // works fine!
integerList.toArray(new Number[0]) // works fine!
integerList.toArray(new Object[0]) // works fine!
and no other array type arguments (since Integer only has those 3 types as super). That is, you're trying to prevent this from compiling:
integerList.toArray(new String[0]) // trying to prevent this from compiling
because, by your argument, String is not a super of Integer. However, Object is a super of Integer, and a String[] is an Object[], so the compiler still would let the above compile, even if hypothetically you can do <S super T>!
So the following would still compile (just as the way they are right now), and ArrayStoreException at run-time could not be prevented by any compile-time checking using generic type bounds:
integerList.toArray(new String[0]) // compiles fine!
// throws ArrayStoreException at run-time
Generics and arrays don't mix, and this is one of the many places where it shows.
A non-array example
Again, let's say that you have this generic method declaration:
<T super Integer> void add(T number) // hypothetical! currently illegal in Java
And you have these variable declarations:
Integer anInteger
Number aNumber
Object anObject
String aString
Your intention with <T super Integer> (if it's legal) is that it should allow add(anInteger), and add(aNumber), and of course add(anObject), but NOT add(aString). Well, String is an Object, so add(aString) would still compile anyway.
See also
Java Tutorials/Generics
Subtyping
More fun with wildcards
Related questions
On generics typing rules:
Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
java generics (not) covariance
What is a raw type and why shouldn’t we use it?
Explains how raw type List is different from List<Object> which is different from a List<?>
On using super and extends:
Java Generics: What is PECS?
From Effective Java 2nd Edition: "producer extends consumer super"
What is the difference between super and extends in Java Generics
What is the difference between <E extends Number> and <Number>?
How can I add to List<? extends Number> data structures? (YOU CAN'T!)
As no one has provided a satisfactory answer, the correct answer seems to be "for no good reason".
polygenelubricants provided a good overview of bad things happening with the java array covariance, which is a terrible feature by itself. Consider the following code fragment:
String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;
This obviously wrong code compiles without resorting to any "super" construct, so array covariance should not be used as an argument.
Now, here I have a perfectly valid example of code requiring super in the named type parameter:
class Nullable<A> {
private A value;
// Does not compile!!
public <B super A> B withDefault(B defaultValue) {
return value == null ? defaultValue : value;
}
}
Potentially supporting some nice usage:
Nullable<Integer> intOrNull = ...;
Integer i = intOrNull.withDefault(8);
Number n = intOrNull.withDefault(3.5);
Object o = intOrNull.withDefault("What's so bad about a String here?");
The latter code fragment does not compile if I remove the B altogether, so B is indeed needed.
Note that the feature I'm trying to implement is easily obtained if I invert the order of type parameter declarations, thus changing the super constraint to extends. However, this is only possible if I rewrite the method as a static one:
// This one actually works and I use it.
public static <B, A extends B> B withDefault(Nullable<A> nullable, B defaultValue) { ... }
The point is that this Java language restriction is indeed restricting some otherwise possible useful features and may require ugly workarounds. I wonder what would happen if we needed withDefault to be virtual.
Now, to correlate with what polygenelubricants said, we use B here not to restrict the type of object passed as defaultValue (see the String used in the example), but rather to restrict the caller expectations about the object we return. As a simple rule, you use extends with the types you demand and super with the types you provide.
The "official" answer to your question can be found in a Sun/Oracle bug report.
BT2:EVALUATION
See
http://lampwww.epfl.ch/~odersky/ftp/local-ti.ps
particularly section 3 and the last paragraph on page 9. Admitting
type variables on both sides of subtype constraints can result in a
set of type equations with no single best solution; consequently,
type inference cannot be done using any of the existing standard
algorithms. That is why type variables have only "extends" bounds.
Wildcards, on the other hand, do not have to be inferred, so there
is no need for this constraint.
####.### 2004-05-25
Yes; the key point is that wildcards, even when captured, are only used
as inputs of the inference process; nothing with (only) a lower bound needs
to be inferred as a result.
####.### 2004-05-26
I see the problem. But I do not see how it is different from the problems
we have with lower bounds on wildcards during inference, e.g.:
List<? super Number> s;
boolean b;
...
s = b ? s : s;
Currently, we infer List<X> where X extends Object as the type of the
conditional expression, meaning that the assignment is illegal.
####.### 2004-05-26
Sadly, the conversation ends there. The paper to which the (now dead) link used to point is Inferred Type Instantiation for GJ. From glancing at the last page, it boils down to: If lower bounds are admitted, type inference may yield multiple solutions, none of which is principal.
The only reason is it makes no sense when declaring a type parameter with a super keyword when defining at a class level.
The only logical type-erasure strategy for Java would have been to fallback to the supertype of all objects, which is the Object class.
A great example and explanation can be found here:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#Why%20is%20there%20no%20lower%20bound%20for%20type%20parameters?
A simple example for rules of type-erasure can be found here:
https://www.tutorialspoint.com/java_generics/java_generics_type_erasure.htm#:~:text=Type%20erasure%20is%20a%20process,there%20is%20no%20runtime%20overhead.
Suppose we have:
basic classes A > B > C and D
class A{
void methodA(){}
};
class B extends A{
void methodB(){}
}
class C extends B{
void methodC(){}
}
class D {
void methodD(){}
}
job wrapper classes
interface Job<T> {
void exec(T t);
}
class JobOnA implements Job<A>{
#Override
public void exec(A a) {
a.methodA();
}
}
class JobOnB implements Job<B>{
#Override
public void exec(B b) {
b.methodB();
}
}
class JobOnC implements Job<C>{
#Override
public void exec(C c) {
c.methodC();
}
}
class JobOnD implements Job<D>{
#Override
public void exec(D d) {
d.methodD();
}
}
and one manager class with 4 different approaches to execute job on object
class Manager<T>{
final T t;
Manager(T t){
this.t=t;
}
public void execute1(Job<T> job){
job.exec(t);
}
public <U> void execute2(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
public <U extends T> void execute3(Job<U> job){
U u= (U) t; //not safe
job.exec(u);
}
//desired feature, not compiled for now
public <U super T> void execute4(Job<U> job){
U u= (U) t; //safe
job.exec(u);
}
}
with usage
void usage(){
B b = new B();
Manager<B> managerB = new Manager<>(b);
//TOO STRICT
managerB.execute1(new JobOnA());
managerB.execute1(new JobOnB()); //compiled
managerB.execute1(new JobOnC());
managerB.execute1(new JobOnD());
//TOO MUCH FREEDOM
managerB.execute2(new JobOnA()); //compiled
managerB.execute2(new JobOnB()); //compiled
managerB.execute2(new JobOnC()); //compiled !!
managerB.execute2(new JobOnD()); //compiled !!
//NOT ADEQUATE RESTRICTIONS
managerB.execute3(new JobOnA());
managerB.execute3(new JobOnB()); //compiled
managerB.execute3(new JobOnC()); //compiled !!
managerB.execute3(new JobOnD());
//SHOULD BE
managerB.execute4(new JobOnA()); //compiled
managerB.execute4(new JobOnB()); //compiled
managerB.execute4(new JobOnC());
managerB.execute4(new JobOnD());
}
Any suggestions how to implement execute4 now ?
==========edited =======
public void execute4(Job<? super T> job){
job.exec( t);
}
Thanks to all :)
========== edited ==========
private <U> void execute2(Job<U> job){
U u= (U) t; //now it's safe
job.exec(u);
}
public void execute4(Job<? super T> job){
execute2(job);
}
much better, any code with U inside execute2
super type U becomes named !
interesting discussion :)
I really like the accepted answer, but I would like to put a slightly different perspective on it.
super is supported in a typed parameter only to allow contravariance capabilities. When it comes to covariance and contravariance it's important to understand that Java only supports use-site variance. Unlike Kotlin or Scala, which allow declaration-site variance. Kotlin documentation explains it very well here. Or if you're more into Scala, here's one for you.
It basically means that in Java, you can not limit the way you're gonna use your class when you declare it in terms of PECS. The class can both consume and produce, and some of its methods can do it at the same time, like toArray([]), by the way.
Now, the reason extends is allowed in classes and methods declarations is because it's more about polymorphism than it is about variance. And polymorphism is an intrinsic part of Java and OOP in general: If a method can accept some supertype, a subtype can always safely be passed to it. And if a method, at declaration site as it's "contract", should return some supertype, it's totally fine if it returns a subtype instead in its implementations