Java Generics, Typ Inferenz, Vererbung?

9

Ich lese über Typinferenz für Generika, und dieser Code wurde als ein Beispiel bereitgestellt, das nicht kompiliert werden kann.

%Vor%

Und die Erklärung in dem Buch war:

  

"(Es sieht so aus, als ob die Methode setError() in StrLastError setError() in der Klasse LastError überschreibt. Dies ist jedoch nicht der Fall. Zum Zeitpunkt der Kompilierung ist das Wissen vom Typ S Der Compiler zeichnet die Signaturen dieser beiden Methoden daher als setError(String) in der Oberklasse und setError(S_extends_CharSequence) in der Unterklasse auf und behandelt sie als überladene Methoden (nicht überschrieben). In diesem Fall, wenn der Aufruf von setError() ist gefunden, findet der Compiler beide überladenen Methoden, die zu dem mehrdeutigen Methodenaufruffehler führen. "

Ich verstehe wirklich nicht, warum der Typ S zur Kompilierzeit nicht abgeleitet werden kann. String wird übergeben, wenn der Konstruktor der Klasse StrLastError aufgerufen wird. und von den API-Dokumenten implementiert String die Schnittstelle CharSequence , Das heißt also nicht, dass S für <S extends CharSequence> tatsächlich vom Typ String ?

ist

Ich habe das Java-Online-Tutorial zum Thema Generika mehrmals gelesen. Ich habe "Type Inference" und Inheritance überprüft, ich weiß einfach nicht, wie das Ganze funktioniert. Ich brauche wirklich eine Erklärung zu dieser Frage.

Die Punkte, an denen ich festhalte, sind:

  1. Wenn der Subtyp S nicht entscheiden kann, wie kann der Super-Typ T entscheiden, weil die Oberklasse keine Obergrenze hat? Oder schließt es, dass T ist String , weil der Subtyp zuerst den Konstruktor des Supertyps aufruft?
  2. Ich verstehe, wenn der Konstruktor aufgerufen wird als:

    %Vor%

    Es wird keine Mehrdeutigkeit geben, da es dann eine einfache Methode ist, die Vorrang hat. (Oder liege ich hier falsch?)

Allerdings, wie ich zu Beginn gesagt habe, kann, wenn String übergeben wird, warum nicht S als String ?

hergeleitet werden     
George Chou 25.04.2015, 14:24
quelle

3 Antworten

2
  

Wenn der Subtyp S nicht entscheiden kann, wie kann der Super-Typ T entscheiden, weil die Oberklasse keine Obergrenze hat? Oder schließt es, dass T String ist, weil der Subtyp zuerst den Konstruktor des Supertyps aufruft?

Der Super-Typ entscheidet nicht oder leitet nicht ab, was T ist; Sie sagen explizit sagen was T durch diese Deklaration ist:

%Vor%

T ist jetzt an String für LastError gebunden, was jede Bezugnahme auf T in der Elternklasse zu einer konkreten String macht.

Ihre untergeordnete Klasse verfügt jetzt über eine gebundene S extends CharSequence , die jedoch an die der übergeordneten Klasse zugewiesenen Grenzen gebunden ist.

Was nun passiert, ist, dass Java Ihre untergeordnete Klasse kompiliert, und das Ergebnis Ihrer untergeordneten Klasse ist, dass zwei Methoden mit einer Signatur, die mit String übereinstimmt, erstellt werden. (Wichtiger Hinweis hier: A String is -a CharSequence . )

In Ihrer Kindklasse wird setError(Ljava/lang/CharSequence;)V als Signatur für setError generiert. Aufgrund der Art, wie Generika funktionieren, wird LastError#setError behandelt , als hätte es eine Signatur von setError(Ljava/lang/String;)V . Dies ist auch der Grund, warum Sie, wenn Sie die Methode überschreiben, einen String -Typ als Parameter anstelle von etwas anderem einfügen.

Also, was wir erreichen, sind zwei Methoden die Override-äquivalente Signaturen haben.

%Vor%

JLS 8.4.8.4. gilt hier.

  

Es ist möglich, dass eine Klasse mehrere Methoden mit erbt   override-äquivalente Signaturen (§8.4.2).

     

Es ist ein Fehler bei der Kompilierung, wenn eine Klasse C eine konkrete Methode erbt   deren Signatur eine Untersignatur einer anderen ererbten konkreten Methode ist   von C. Dies kann passieren, wenn eine Oberklasse generisch ist und zwei hat   Methoden, die in der generischen Deklaration unterschieden wurden, aber die   gleiche Signatur im verwendeten Aufruf.

  

Ich verstehe, dass wenn der Konstruktor aufgerufen wird als StrLastError err = new StrLastError & lt; & gt; ((CharSequence) "Fehler"); es wird keine Zweideutigkeit geben, da seine einfache Methode dann übergeht. (Oder ich liege hier sogar falsch)

Nein, jetzt mischen Sie mit rohen Typen . Interessanterweise wird funktionieren , hauptsächlich weil die Signaturen für die beiden Methoden zu:

geworden sind %Vor%

Sie möchten Generika verwenden, um ein solches Szenario zu vermeiden. Vielleicht möchten Sie die Superklassenmethode irgendwann aufrufen, aber in diesem Szenario mit diesen Bindungen ist es sehr schwer zu erreichen.

    
Makoto 25.04.2015, 15:30
quelle
4

Sie müssen sich daran erinnern, dass die Klassen einzeln zusammengestellt werden. Java Generics sind keine Templates wie in anderen Sprachen. Es gibt nur eine kompilierte Klasse und nicht eine Klasse pro Typ, mit dem sie verwendet wird.

Auf diese Weise können Sie sehen, dass die Klasse StrLastError so kompiliert werden muss, dass sie auch mit anderen Klassen verwendet werden kann, die CharSequence als generischen Typ S implementieren.

Das ist der Grund, warum der Compiler zwei verschiedene Methoden anstelle einer überschriebenen Methode erhält. Jetzt wäre es ein Laufzeit-Job, zu sehen, dass die Unterklasse die Methode im Elternteil möglicherweise gerade in den Fällen überschreiben wollte, in denen die Typen dies vorschlagen. Da dieses Verhalten für den Entwickler schwer zu verstehen ist und möglicherweise zu Programmierfehlern führen würde, löst es eine Ausnahme aus.

Wenn Sie CharSequence als generischen Typparameter der Klasse StrLastError verwenden, rufen Sie die Methode setError in der Elternklasse auf, da der Typ von "Last Error" String ist, was spezifischer als% ist. co_de% und Java wählt immer die spezifischste Methode, falls sie überladen ist. (Ich hoffe, es ist klar, dass die Methode auch in diesem Fall nicht übersteuert wurde)

    
muued 25.04.2015 14:39
quelle
-2

Dies ist ein kniffliges Beispiel, um es zu beheben, müssen Sie die folgende Zeile ändern:

%Vor%     
Crazyjavahacking 25.04.2015 14:32
quelle

Tags und Links