Today I ran into the following problem. Consider the setup:
interface A {
void foo();
}
interface B {
void bar();
}
class Impl implements A,B {
public void foo() { }
public void bar() { }
}
class Usage {
void worksAsParameter(){
acceptIt(new Impl());
}
<T extends A & B> void acceptIt(T foo){
}
<T extends A & B> T returnIt(){
return new Impl(); // <-- Compile error
}
}
The code compiles except in the last statement as marked. Eclipse gives me the error: Type mismatch: cannot convert from Impl to T
My question is: Why is Impl assignable to T when given as a parameter (shown in worksAsParameter but not when T is a return type ?
And also, what expression aside from null will satisfy the type T in the case where Impl does not?
Please note that this question is not the same as this SO question although similar.
Edit: Fixed typo.
=== Summary ===
It would seem I had misunderstood how generic return types work. I will try to write up my new understanding of it.
Lets look at the issue:
<T extends A & B> T returnIt(){
return new Impl(); // <-- Compile error
}
My initial assumption was that the implementing class (in this case Usage) decided on the concrete type for T with the restriction that it must extend A and B. Apparently it is the caller/callsite that gets to decide what T is and Usage must supply a value that is assignable to T. However, as T is a compile time deal only it is impossible to supply such a value aside from null (as it is assignable to anything).
Afaik this means that any code of the form will only ever be able to return null:
<T extends A> T returnIt(){
return x; // <-- Compile error
}
A fairly unintuitive feature that is hopefully more useful in a different setting. Thanks Peter!
The reason
doesn’t compile is that T could be any class which extends A and B. You happen to know there is only one possible class at the moment, but the compile doesn’t “know” this.
e.g.
You can force the issue with
but a better solution is
This defines two generics
T extends Aand alsoB extends ObjectWhat you may have intended is
where T must extend A and B.