Java Generics: Wie funktioniert Method Inference, wenn Platzhalter in den Methodenparametern verwendet wird?

8

Angeblich habe ich folgendes:

%Vor%

Schauen wir uns zunächst Abschnitt 5 an, der die Schlussfolgerung bei der Arbeit zeigt. Jetzt Abschnitt 5 Abschnitt ist ein funktionierender Abschnitt.

Wenn es das ist, warum ist dann die Klausel (1) & amp; (2) Fehler haben?

Aus meiner Sicht wird für alle diese 3 Aufrufmethoden die gleiche Folgerung erzeugt, da beim Aufruf der abc-Methode keine tatsächlichen Typparameter verwendet werden.

Methodenparameter & lt; T & gt; abc (Liste & lt; T & gt; a, T b & gt;)
abgeleitete & lt; Objekt & gt; abc (Liste & lt; Objekt & gt ;, Objekt) // (4)

Bitte beachten Sie, dass die Methode abc () und def () meine Methode ist. Der Compiler weiß nicht, was ich mit der Liste in dieser Methode machen möchte. Ich könnte nur die Listengröße drucken oder vielleicht gar nichts tun, wie oben gezeigt. Also gibt es hier kein Eingreifen oder Eingreifen.

FORTSETZUNG - & gt; Das wird sehr verwirrend für mich.

%Vor%

Beachten Sie, dass Klausel 21 dasselbe wie Klausel 20 ist, mit der Ausnahme, dass der zweite Parameter als Liste anstelle von Ganzzahl erstellt wird.

Klausel 20 ist ok cos 'T wird als Objekt erkannt.
Klausel 22 ist in Ordnung. Der gleiche Grund wie in Klausel 20.
Klausel 21 ist fehlgeschlagen? Man könnte auch sagen, dass T Objekt ist - würde auch funktionieren?

    
yapkm01 26.09.2011, 15:18
quelle

5 Antworten

1

Sie haben ein bisschen Strohmann geschaffen, indem Sie jeweils ein LinkedList<Object> erstellt haben. Das kann das Problem erschweren. Was Sie beachten müssen ist, dass wenn der Compiler diese Methodenaufrufe erhält, er nicht weiß, dass Sie eine LinkedList<Object> erstellt haben. Es könnte zum Beispiel ein LinkedList<Integer> sein.

Sehen wir uns nun Ihren Code mit interessanteren Initialisierungen an:

%Vor%

Es braucht nicht viel zu sehen, wenn die Implementierung von abc dies ist:

%Vor%

Damit würden Sie ClassCastException erhalten, wenn Sie i1 initialisieren.

  

Aus meiner Sicht hat all diese 3 Aufrufmethoden folgende Folgerung erzeugt, da beim statischen Methodenaufruf abc keine eigentlichen Typparameter verwendet werden.

%Vor%

Das ist kategorisch falsch. Es wird nicht abgeleitet, dass T Object in any Ihrer Beispiele ist, nicht einmal im Fall von ? super Object . T wird zum Capture von a aufgelöst, und es sei denn, Sie können diesem Capture einen String zuweisen (wie es bei ? super Object der Fall ist), dann haben Sie einen Typfehler.

Bearbeiten # 1

Was Ihr Update anbelangt (Ich habe Ihr generisches Array durch ein List<T> ersetzt, da generische Arrays das Problem unnötig verschleiern):

%Vor%

Das ist nicht korrekt. Der entscheidende Fehler, den Sie machen, ist hier:

%Vor%

Dies ist überhaupt nicht was die Capture-Engine in Ihrem Aufruf ausführt oder übersetzen sollte. Es löst T als <? extends Object> auf, aber als eine bestimmte Erfassung von <? extends Object> . Nennen wir es capture-1-of<? extends Object> . Also muss Ihre Methode so sein:

%Vor%

Dies bedeutet, dass eine Bindung zwischen den beiden Parametern besteht ... sie müssen zu demselben Capture aufgelöst werden. Im Allgemeinen ist es sehr schwierig, dem Compiler zu sagen, dass zwei Dinge dasselbe Capture sind. Tatsächlich ist selbst dies kein gültiger Aufruf von ppp (obwohl sie eindeutig die gleiche Erfassung sind):

%Vor%

Eine Möglichkeit, ppp aufzurufen, ist über einen generischen Vermittler:

%Vor%

Die only sichere Methode, wie Sie ppp mit einer Liste mit wildbehauenen Objekten aufrufen könnten, würde lauten:

%Vor%

Das liegt daran, dass das null das einzige ist, was Sie irgendetwas zuweisen können. Auf der anderen Seite, wenn Sie diese Methode hatten:

%Vor%

Sie könnten es tatsächlich so aufrufen:

%Vor%

Weil in dieser Fall die Ableitung T zu Objekt generalisieren kann. Da List<? extends Integer> nicht mit List<Object> kovariant ist, kann dies für ppp() nicht der Fall sein.

Aber , was die meisten Leute tun, um dies zu umgehen, ist, ihre Methodensignatur zu lockern. Erklären Sie stattdessen ppp wie folgt:

%Vor%

Dies folgt den Richtlinien, die Sean in seinen Beitrag von "PECS" gestellt hat.

  

Wenn (Ihre Methode) produziert , verwenden Sie erweitert . Wenn verbraucht ist, verwenden Sie super . p>

Bearbeiten # 2

Bezüglich deiner letzten Änderung:

%Vor%

Object ist keine gültige Inferenz für T . Ich denke, das ist etwas Grundlegendes, das du vermisst, also werde ich es klar sagen:

A List<Integer> ist NICHT typenzuweisbar für List<Object>

Überhaupt nicht. Wenn es so wäre, könntest du so etwas tun, was offensichtlich die Typsicherheit verletzt:

%Vor%

So T kann nicht als Object abgeleitet werden, da es erforderlich wäre, List<Integer> einer Variablen vom Typ List<Object> zuzuweisen.

    
Mark Peters 26.09.2011, 16:46
quelle
5

Das Schwierige an der Platzhalterkarte ist zu erkennen, dass ? extends Foo nicht "irgendetwas, das Foo erweitert" bedeutet, sondern stattdessen "ein bestimmter Typ, der Foo erweitert" bedeutet. Und da Sie sich außerhalb dieser Definition befinden, haben Sie keine Möglichkeit, zu wissen, welcher spezifische Untertyp von Foo ist.

Aktualisierung:

Wie gesagt, es ist kompliziert. Hier sind einige Kommentare zu Ihrem Code.

%Vor%

Update 2:

Das Problem hier ist, dass Sie es mit festen, aber nicht löschbaren Variablen zu tun haben.

%Vor%

Lesen Sie diese Frage, es könnte etwas Licht auf das Problem werfen:

Was ist PECS (Producer erweitert Consumer Super)?

    
Sean Patrick Floyd 26.09.2011 15:21
quelle
0
  

Ein Platzhalter würde dann benötigt, um die Kovarianz des Subtyps zu induzieren

Ich würde eher "simulieren" sagen, da Sie selbst nach der Verwendung von Joker-Karten nicht die gleiche Funktionalität erhalten, die Sie für Arrays erhalten.

  

Dann ist die Frage, warum Klausel (3) funktioniert und nicht Klausel (2) oder (1)?

Betrachten Sie die erste Erklärung:

  

List <?> a = new LinkedList<Object>();

Diese Deklaration besagt effektiv, dass ich wirklich nicht weiß (oder interessiert), welche Art von Element die Sammlung a enthält. Dies bedeutet, dass Sie die Sammlung nicht "mutieren", da Sie möglicherweise Elemente vom Typ hinzufügen, die nicht wirklich mit a kompatibel sind. Sie können List<?> a = new ArrayList<String>() haben, aber Sie können immer noch nichts einfügen. Wenn ein Add zulässig ist, kann der Compiler die Typsicherheit der Auflistung nicht garantieren.

  

List <? extends Object> b = new LinkedList<Object>();

Hier sagen Sie b ist eine Sammlung, die Elemente enthält, die Object erweitern. Was für ein Element, weißt du nicht. Dies wiederum wie in der vorherigen Diskussion erlaubt es Ihnen nichts hinzuzufügen, da Sie am Ende die Sicherheit des Typs gefährden könnten.

  

List <? super Object> c = new LinkedList<Object>();

Hier sagen Sie, dass c eine Sammlung ist, die Elemente vom Typ Object und ihre Super-Klassen oder mit anderen Worten mindestens Object enthält. Da jeder Referenztyp in Java mit Object zuweisungskompatibel ist, funktioniert er im dritten Fall.

    
Sanjay T. Sharma 26.09.2011 15:36
quelle
0

Integer[] ist ein Untertyp von Object[] , jedoch ist List<Integer> kein Untertyp von List<Object> . Das ist ziemlich verwirrend; Arrays sind primitiver und sollten vermieden werden.

Wenn ein Methodenparametertyp T[] ist, akzeptiert er alle S[] , wobei S ein Untertyp von T ist.

Um dies mit List zu tun, sollte der Typ List<? extends T> sein. Es akzeptiert alle List<S> , wobei S ein Subtyp von T ist.

    
irreputable 26.09.2011 20:48
quelle
0

List<?> a bedeutet, dass a einen bestimmten Typ enthält, der unbekannt ist. Betrachten Sie dieses vollständigere Beispiel:

%Vor%

List<? extends Object> bedeutet im Wesentlichen dasselbe wie List<?> und würde den gleichen Fehler verursachen.

List<? super Object> bedeutet, dass die Liste einen bestimmten, aber unbekannten Super-Typ von Object enthält, und die parametrisierte Methode kann jedes Objekt akzeptieren, das -a Object für den zweiten Parameter ist. Während der Methodenaufruf typsicher ist, verursacht der Versuch, c einen unsicheren Wert zuzuweisen, einen Fehler:

%Vor%     
erickson 26.09.2011 16:00
quelle

Tags und Links