Reputation: 6440
I'm a bit at a loss with generics. I have the following code:
public interface SampleValue<T> {
T getValue();
}
public static class SampleBoolean implements SampleValue<Boolean> {
@Override
public Boolean getValue() {
return Boolean.TRUE;
}
}
public static final class SampleValueGenerator {
private SampleValueGenerator() {
// do not call
}
public static <T, R extends SampleValue<T>> R forType(Class<T> clazz) {
if(Boolean.class.equals(clazz)) {
return new SampleBoolean();
}
}
}
When I try this, IntelliJ (i.e. the compiler) tells me that R
and SampleBoolean
are incompatible types (for the return
line).
When I try the non-generic (raw) return type
public static <T> SampleValue forType(Class<T> clazz) {
I don't get any error;
public static <T, R extends SampleValue<?>> R forType(Class<T> clazz) {
(with the ?
wildcard) however fails again. And for
public static <T> SampleValue<T> forType(Class<T> clazz) {
I get Incompatible types, Found: SampleBoolean, Required: SampleValue<T>
.
My guess is that it has to do with e.g. List not being an ancestor of List (but a sibling), but I fail to see the wood for the trees with the above.
Can someone please explain what's going on, and why the long example doesn't work?
Update: NB: The idea was to have a few more if/else branches for different types, but I stopped when the compiler started complaining...
Upvotes: 0
Views: 122
Reputation: 1070
The R you are returning is always an R that implements SampleValue of Boolean and not an R that implements SampleValue of T (generic type that is set in runtime).
// if i do this
SampleValueGenerator.forType(Integer.class)
// i am expecting something that implements SampleValue<Integer>
// but you are always returning something that implements SampleValue<Boolean>
EDIT This should work (did not test yet)
public static <T, R extends SampleValue<T>> R forType(Class<T> clazz) {
return () -> {
try{
return (T)clazz.newInstance(); // Also clazz should have a default constructor.
}catch(Excepetion e){
// This catch block should be for NoSuchMethodException and InstantionException
}
}
}
Upvotes: 0
Reputation: 4707
The reason is that your conditional doesn't prove anything to the compiler.
The confusion you're having here involves your conditional:
if(Boolean.class.equals(clazz))
With this check, you're inferring that T
is a Boolean
, but the compiler has no way of enforcing this. The compiler doesn't implicitly assume that this check will ensure T
is Boolean
. (All the compiler knows about equals
in the context of this method is that it returns a boolean
.)
Therefore, despite your check, R
and SampleBoolean
are incompatible types because R extends SampleValue<T>
while T
can be anything at all.
I can't really come up with a way to ensure a return of new SampleValue<T>
based on T
but if I do I will edit this answer with a solution. I'd love to see ideas from others about it.
Upvotes: 2
Reputation: 583
I think the problem is that SampleBoolean
implements SampleValue<Boolean>
which is a specific type and not something generic. On the other hand, R is declared to extend a generic type SampleValue<T>
.
SampleValue<T>
and SampleValue<Boolean>
are two different types, so this is why you get that compilation error. The forType
function wants to return a generic type R and you return a specific type with the following statement:
return new SampleBoolean();
Upvotes: 1