Heute bin ich auf das folgende Problem gestoßen. Betrachten Sie den Aufbau:
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
}
}
Der Code lässt sich mit Ausnahme der letzten markierten Anweisung kompilieren. Eclipse gibt mir die error: Type mismatch: cannot convert from Impl to T
Meine Frage ist: Warum ist Impl
zuordenbar zu T
wenn sie als Parameter angegeben wird (gezeigt in worksAsParameter
aber nicht, wenn T
ist ein Rückgabetyp ? Und außerdem, welcher Ausdruck außer dem null
erfüllt den Typ T
für den Fall, dass Impl
nicht?
Bitte beachten Sie, dass diese Frage nicht gleichbedeutend ist mit diese SO-Frage obwohl sie ähnlich sind.
Edit: Tippfehler korrigiert.
\=== Zusammenfassung ===
Wie es scheint, hatte ich die Funktionsweise der generischen Rückgabearten falsch verstanden. Ich werde versuchen, mein neues Verständnis davon aufzuschreiben.
Schauen wir uns das Problem an:
<T extends A & B> T returnIt(){
return new Impl(); // <-- Compile error
}
Meine anfängliche Vermutung war, dass die implementierende Klasse (in diesem Fall Usage
) entschied sich für den konkreten Typ für T
mit der Einschränkung, dass sie sich erstrecken muss A
y B
. Offenbar ist es der Anrufer/die Anrufstelle, der/die entscheidet, was T
ist und Usage
muss einen Wert liefern, der zuordenbar ist zu T
. Da jedoch T
ist eine Sache der Kompilierzeit, denn es ist nicht möglich, einen solchen Wert zu liefern, abgesehen von null
(da es allem zuordenbar ist). Afaik bedeutet dies, dass jeder Code der Form immer nur in der Lage sein wird, zurückzugeben null
:
<T extends A> T returnIt(){
return x; // <-- Compile error
}
Eine ziemlich unintuitive Funktion, die hoffentlich in einer anderen Umgebung nützlicher ist. Danke Peter!