I have the following code:
Collection<Something<ConcreteA, ConcreteB>> methodOne() {
....
}
void methodTwo(Collection<Something<ConcreteA, ?>> val) {
....
}
// This call generates an error.
methodTwo(methodOne());
The methodTwo(methodOne())
generates an error: incompatible types: Collection<Something<ConcreteA,ConcreteB>> cannot be converted to Collection<Something<ConcreteA,?>>
.
I understand that I can just cast methodTwo((Collection) methodOne())
and everything works fine except for the Unchecked assignment
warning which can be ignored. But that defeats the purpose of using generics. Changing methodTwo
signature from capture to Object also does not help, i.e. methodTwo(Collection<Something<ConcreteA, Object>> val)
also produces a similar error.
What am I doing wrong? What is the right way of dealing with it?
CodePudding user response:
You might want to check this question for details on using wildcard in nested types.
In short, what you're trying to do is not type safe with wildcard. And it doesn't work, because in Java generics (e.g. Collection<T>
) are invariant.
To quote @irreputable's answer (modified):
Suppose
D
is subtype ofB
:B x = new D(); // OK Collection<B> y = new ArrayList<B>(); // OK ArrayList<B> y = new ArrayList<D>(); // FAIL
Now,
Something<ConcreteA, ConcreteB>
is a subtype ofSomething<ConcreteA, ?>
, thereforeSomething<ConcreteA, ?> x = new Something<ConcreteA, ConcreteB>(); // OK ArrayList<Something<ConcreteA, ?>> y = new ArrayList<Something<ConcreteA, ConcreteB>>(); // FAIL
The solution in your case would be to make methodTwo
generic, as @RobertHarvey suggested:
void methodTwo <K>(Collection<Something<ConcreteA, K>> val)
This way Something<ConcreteA, K>
type is fixed to type you're actually passing (as opposed to being its subtype), and the type safety is guaranteed.
CodePudding user response:
Generics are invariant. Unless the top level type argument is wildcard, the type arguments must be identical.
Collection<Something<ConcreteA, ConcreteB>>
is NOT a subtype of Collection<Something<ConcreteA, ?>>
, even though Something<ConcreteA, ConcreteB>
is a subtype of Something<ConcreteA, ?>
, for the same reason that Collection<String>
is not a subtype of Collection<Object>
even though String
is a subtype of Object
.
If you want to accept potentially different type parameters, you can put a wildcard in the top level, like this:
void methodTwo(Collection<? extends Something<ConcreteA, ?>> val) { }