This question already has answers here:
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
(19 answers)
Closed 4 years ago.
I am in the creation of a small application and I stumbled over the following problem.
There is a List<Class<MyCustomBaseClass>> in my application and a function with the signature public <T extends MyCustomBaseClass> void addClass(Class<T> clazz).
The AddClass should put the clazz into the List. But I get the following error there:
The method add(Class<MyCustomBaseClass>) in the type List<Class<MyCustomBaseClass>> is not applicable for the arguments (Class<T>)
Here are my 3 classes as simplified as I could make them:
// Program.java
package me.mischa.stackoverflow;
import java.util.ArrayList;
import java.util.List;
public class Program {
private List<Class<MyCustomBaseClass>> _listOfClasses;
private static Program _instance;
public Program() {
_listOfClasses = new ArrayList<>();
}
public static void main(String[] args) {
Program program = new Program();
program.addClass(MyCustomChildClass.class);
}
public <T extends MyCustomBaseClass> void addClass(Class<T> clazz) {
_listOfClasses.add(clazz);
}
}
.
// MyCustomBaseClass.java
package me.mischa.stackoverflow;
public class MyCustomBaseClass {
}
.
// MyCustomChildClass.java
package me.mischa.stackoverflow;
public class MyCustomChildClass extends MyCustomBaseClass {
}
The error is at the line _listOfClasses.add(clazz);
I do not understand why <T extends MyCustomBaseClass> should not be compatible with <MyCustomBaseClass>
Java's generics are invariant. That means that, as a type parameter, Class<MyCustomBaseClass> means exactly that, no Class object representing a subclass of MyCustomBaseClass is allowed.
In your addClass method, you've only given an upper bound on T when defining it -- T could be a subclass of MyCustomBaseClass, e.g. your class MyCustomChildClass. The compiler disallows this call because of the mismatch.
You can widen what's allowed in _listOfClasses by providing a matching upper bound, which will allow the method addClass to compile.
private List<Class<? extends MyCustomBaseClass>> _listOfClasses;
Incidentally, because it doesn't really matter exactly what type T is in addClass, you can remove it and use a wildcard.
public void addClass(Class<? extends MyCustomBaseClass> clazz) {
Java's generics are invariant. There's a reason for that. Imagine the following code (NOTE: Number is a supertype of both Integer and Double):
List<Double> doublesOnly = new ArrayList<Double>();
List<Number> numbers = doublesOnly;
numbers.add(new Integer(5));
Double d = doublesOnly.get(0);
In the above, if it had been valid java, you are assigning an Integer to a Double, which is a problem because an Integer isn't a Double. This is why in actual fact, if you attempt to compile the above, it won't work; the second line is marked as invalid java, because a List<Double> cannot be assigned to a List<Number>. There is a solution:
List<Double> doublesOnly = new ArrayList<Double>();
List<? extends Number> numbers = doublesOnly;
numbers.add(new Integer(5));
Double d = doublesOnly.get(0);
This time, line 3 is the error: You cannot add anything to a List<? extends Something>, other than null. There's no way to fix this code and that's good because we're doing something fundamentally bad.
The solution in your specific case is two-fold:
More generally you should avoid the notion of using Class<?> in your APIs. Generally, use factories instead.
If you must, try something like: List<Class<? extends MyCustomBaseClass>>. Yes, 2 extends. You can add a Class<MyCustomChildClass> to this list.
Related
This question already has answers here:
Use of '? extends ' and '? super ' in Collection generics [duplicate]
(7 answers)
Closed 1 year ago.
I have 2 classes that are linked using Generic Wildcards
import java.util.*;
public class Main
{
public static void main(String[] args){
AnotherClass another = new AnotherClass();
List<Integer> a1 = new ArrayList<Integer>();
a1.add(1);
another.runtest(a1);
}
}
import java.util.*;
public class AnotherClass {
public void runtest(List<? extends Number> a){
a.add(2);
}
}
But while executing the above code I am getting exceptions as below:
AnotherClass.java:4: error: no suitable method found for add(int)
a.add(2);
^
method Collection.add(CAP#1) is not applicable
(argument mismatch; int cannot be converted to CAP#1)
method List.add(CAP#1) is not applicable
(argument mismatch; int cannot be converted to CAP#1)
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
Can someone help me to write the Code in better way or how can we use wildcards during method call. I am not able to understand why it is now allowing extends in method definition.
List<? extends Number> is a list which contains instances of some subclass of Number - significantly, you don't know which. It could be a List<Integer>, a List<Double>, even List<MyCustomImplementationOfNumber>.
For example, a List<Double> is expected only to contain instances of Double (or null). If you were allowed to add an Integer to that list on this method, you would be violating this expectation on the subsequent contents of the Double.
The compiler doesn't know if it is safe to add an Integer to it.
The fix is to pass a list to which it is known to be safe to add an Integer. Any of the following would work (amongst others):
public void runtest(List<Integer> a){
public void runtest(List<Number> a){
public void runtest(List<? super Integer> a){
public void runtest(List<? super Number> a){
A super bound (a lower bound) says "I don't know the exact bound on the type; but I know it is some superclass". Since all instances of a subclass are instances of a superclass, it is safe to add instances of the subclass to the list, hence it is allowed.
Ideally in such code as this you would want to read and act upon the data in the list, perhaps removing items, not modify the list by adding items as you will get into issues like the above, because the code cannot guarantee that the autoboxing Integer type can be added to the List, as it could also be an ArrayList<Double>.
If you wish to modify the list you will need to target specific type of numbers, like Integer or Double or Float etc....
In short, my recommendation is, use such a construct for reading/filtering/splitting/deleting, but not for adding items.
This question already has an answer here:
Java generic collection, cannot add list to list
(1 answer)
Closed 5 years ago.
Why it doesn't work ?
class W<A extends Number>
{
public void create(A value)
{
}
}
public void calculate(W<? extends Number> w)
{
Integer v = 5;
w.create(v); // Compilation error
}
Could somebody explain what is wrong with that code and how to fix it?
Compilation error : "create (capture) in W cannot be applied to (java.lang.Integer)"
You have a common misconception about wildcards in generics. You think that ? means you can pass any type (as long as it extends Number), but you cannot.
? extends Number does not mean that you can pass any type that extends Number. It means "a specific, but unknown type that extends Number". Since the exact type is unknown to the compiler, it cannot allow you to use Integer - the compiler at that point simply has not enough information to check if it is type-safe. (It doesn't know if the actual type of the W that you pass to the method is Integer or some other type that extends Number).
This is exactly the same reason as why you cannot add anything to a List<?>, for example, which is a question that people often ask.
This follows straight from the core generics principle. When you use ? extends SomeType, you cannot pass/consume anything into the reference, as this might violate type safety guarantee provided by the Generics when retrieving these items. One would then be able to do -
List<Long> longs = Arrays.asList(5L, 10L, 20L);
List<? extends Number> numbers = longs;
// Trouble!
numbers.add(10.5);
See one of my blog posts here for details on how subtyping works with Generics.
You can use bounderies in the create method:
public <T extends Number> void create(final T value) {
}
Example:
class W<A extends Number> {
public <T extends Number> void create(final T value) {
}
public void calculate(final W<? extends Number> w) {
final Integer v = 5;
w.create(v); // Compilation error
}
}
You have to declare what kind of W you're using before you use it
public void calculate(W<? extends Number> w) {
Integer v = 5;
new W<Integer>().create(v);
}
or, you can change the create method to accept any number
public void create(Number value) {
}
Both objects should extend Number, but that does NOT mean these objects cannot be siblings:
ClassA extends Number {..}
ClassB extends Number {..}
ClassA cannot be casted to ClassB, and this is what could happen in your code. It can be both ClassA but your compiler cannot be sure of that as it could be ClassB or any other class extending Number, so it gives a compile error.
This question already has answers here:
Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
(3 answers)
Closed 8 years ago.
I guess this is a very confusing title, but I don't know what else to call - probably answered somewhere as well, but I couldn't find anything. Take this example:
List<Class<? extends Integer>> myList;
void foo() {
bar(myList);
}
void bar(List<Class<?>> a) { /* ... */ }
It doesn't compile (not applicable arguments, it says). If I remove the bounding it works fine:
List<Class<?>> myList;
void foo() {
bar(myList);
}
void bar(List<Class<?>> a) { /* ... */ }
Class<? extends Integer> is just more specific than Class<?>. How come it stops working? Note that this problem is only in the second level generics. If there wasn't a List, just Class<? extends Integer> and Class<?>, it works as well. But it seems to stop working when the generics is two ore more levels deep. Any reasons/workarounds/etc?
Note that List<B> is not a subtype of List<A> even if B is a subtype of A. Therefore, List<Class<? extends Integer>> is not a subtype of List<Class<?>> even though you can assign a Class<? extends Integer> object to a Class<?> variable. Just consider that in bar, it would be legal to invoke a.add(Object.class), as Class<Object> is a subtype of Class<?>.
You therefore want to extend the bar argument type to List<? extends Class<?>>.
I have a simple generic list class. I'm trying to achieve this: given a list instance of which we know only that it contains instances of a specific subclass of a class, and given an instance of this class, add this instance to the list if it is instance of the contained (subclass) type, otherwise throw an exception (eg. ClassCastException). I tried the following:
class MyList<T>
{
private Class<T> genericClass;
private List<T> list = new ArrayList<>();
public MyList(Class<T> genericClass)
{
this.genericClass = genericClass;
}
public void add(T elem)
{
list.add(elem);
}
//...
public Class<T> getGenericParamClass()
{
return genericClass;
}
}
class A{}
class B extends A{}
class C extends A{}
class Program
{
public static void main(String... args)
{
MyList<B> list1 = new MyList<>(B.class);
MyList<C> list2 = new MyList<>(C.class);
MyList<? extends A> ls = checkStuff() ? list1 : list2;
ls.add(ls.getGenericParamClass().cast(lotsOfStuff())); //ERROR ?!!
}
static boolean checkStuff()
{
Random random = new Random();
return random.nextBoolean();
}
static A lotsOfStuff()
{
return new B();
}
}
I thought that given a Class object whose type parameter is the same as the type of a parameter of a method, I would be able to cast something using the former to be able to pass it to the latter. Alas, it seems I cannot: I get a compile-time error!
I could throw generics out the window, go full unchecked and just say:
A val = lotsOfStuff();
if (myList.getGenericParamClass().isInstance(val))
ls.add(val)
else
throw new SomeException();
But, that would probably create more problems than it would solve, and also it would really bug me.
Am I missing something here, or is it simply not possible the way I thought it out?
Edit:
I understand fully well why something like this cannot work:
List<? extends Number> abc=new ArrayList<Integer>();
abc.add(new Integer(10));
But in my mind, the following transitivity holds: the type of the parameter of add() Is-The-Same-As the type parameter of MyList Is-The-Same-As the type parameter of the Class returned by getGenericParamClass() Is-The-Same-As the return type of the cast() method of that Class. I (as a human) can know that those unknown types are the same, because I am getting them from the same object.
Is there a fault in my logic, or is this a limitation of Java?
The compilation error is:
The method add(capture#2-of ? extends A) in the type MyList is not applicable for the arguments (capture#3-of ? extends A)
To understand that message, recall that ? extends A stands for an unknown type that is a subtype of A. That compiler can not know that the ? extends A returned by lotsOfStuff() is the same (or a subtype of) the ? extends A that the MyList.add method expects, and in fact, your program does not ensure that this is the case (because lotsOfStuff() always returns a B, even if it should be added to a list of C.)
To express that the two are of the same type, we must use a type parameter. The easiest way to get one is to move the code doing the casting and throwing into class MyList<T> (which already has a suitable type parameter), for instance by adding the following method:
void addOrThrow(Object o) {
add(genericClass.cast(o));
}
So I've been reading through the Generics tutorial offered by Oracle here: http://docs.oracle.com/javase/tutorial/java/generics/
And I've tried running my own example to make sure I understand how to use Generics. I have the following code:
import java.util.*;
public class Generics {
class NaturalNumber {
private int i;
public NaturalNumber(int i) { this.i = i; }
}
class EvenNumber extends NaturalNumber {
public EvenNumber(int i) {
super(i);
}
}
public static void main(String[] args) {
Collection<? extends NaturalNumber> c = new ArrayList<>();
c.add(new EvenNumber(2)); //this line produces a compile time error
}
}
My goal is to be able to add any object which is a subtype of NaturalNumber to the Collection c. I'm not sure why this doesn't work and reading through Oracle's tutorial hasn't enlightened me either.
When you have ? extends NaturalNumber, the parameter could be some other subclass of NaturalNumber that is in no way related to EvenNumber. For instance,
Collection<? extends NaturalNumber> c = new ArrayList<OtherNaturalNumber>();
is valid if OtherNaturalNumber extends NaturalNumber.
Consequently, you are not able to add an EvenNumber instance to the list. You can just use this declaration:
Collection<NaturalNumber> c = new ArrayList<>();
which will allow you to add any NaturalNumber instance (including an EvenNumber).
On another note, you probably meant to make those nested classes static (or don't nest them at all).
First Collection<? extends NaturalNumber> should just be Collection<NaturalNumber>. Instances of EvenNumber (or any NaturalNumber or a subtype of NaturalNumber) can be put into the collection this way.
Essentially Collection<? extends NaturalNumber> says that the type of the type parameter to Collection extends NaturalNumber. So say that class OddNumber extends NaturalNumber, then the type of Collection's type parameter could be OddNumber which EvenNumber cannot be safely cast to.
However there is another compiler error. To be used in the static context or main(String[]) each of the inner classes NaturalNumber and EvenNumber need to have the modifier static placed on each class declaration.
Your problem is that you've told the compiler that the Collection's element type can be any type that extends NaturalNumber, but then you tried to insert an object into it. As far as the compiler knows, c is a Collection<OddNumber>, and you just added an EvenNumber!