Generische Typ-Parameter-Folgerung in Methodenverkettung

8

Nachdem ich diese Frage gelesen habe, habe ich begonnen, über generisch nachzudenken Methoden in Java 8 . Was passiert mit generischen Typparametern, wenn Methoden verkettet werden?

Für diese Frage werde ich einige generische Methoden aus Guavas ImmutableMap verwenden, aber meine Frage ist allgemeiner und kann auf alle verketteten generischen Methoden angewendet werden.

Betrachten Sie die generische Methode ImmutableMap.of , die diese Signatur hat:

%Vor%

Wenn wir diese generische Methode verwenden, um Map zu deklarieren, leitet der Compiler die generischen Typen korrekt ab:

%Vor%

Mir ist bewusst, dass der Compiler-Inferenzmechanismus seit Java 8 verbessert wurde, d. h. er leitet die generischen Methodentypen aus dem Kontext ab, der in diesem Fall eine Zuweisung ist.

Der Kontext kann auch ein Methodenaufruf sein:

%Vor%

In diesem Fall werden die generischen Typen von ImmutableMap.of aus den generischen Typen des Arguments von someMethod abgeleitet, d. Map<String, String> map .

Aber wenn ich versuche, ImmutableMap.builder() und chain-Methoden zum Erstellen meiner Map zu verwenden, erhalte ich einen Kompilierungsfehler:

%Vor%

Der Fehler ist:

%Vor%

(Ich habe die Paketnamen aus Gründen der Übersichtlichkeit aus der Fehlermeldung entfernt).

Ich verstehe den Fehler und warum es passiert. Die erste Methode in der Kette ist ImmutableMap.builder() und der Compiler hat keinen Kontext, um auf die Typparameter zu schließen, also fällt er auf <Object, Object> zurück. Dann wird die Methode ImmutableMap.Builder.put mit den Argumenten "a" und "b" aufgerufen, und schließlich wird die Methode ImmutableMap.Builder.build() aufgerufen, die ein ImmutableMap<Object, Object> zurückgibt. Aus diesem Grund erhalte ich den Fehler inkompatible Typen: Wenn ich versuche, diese ImmutableMap<Object, Object> -Instanz meiner Map<String, String> map -Variable zuzuweisen, beschwert sich der Compiler.

Ich weiß sogar, wie ich diesen Fehler beheben kann: Ich könnte entweder die Methodenkette in zwei Zeilen aufteilen, so dass der Compiler nun auf die generischen Typparameter schließen kann:

%Vor%

Oder ich könnte die generischen Typparameter explizit angeben:

%Vor%

Meine Frage lautet also nicht, wie ich dies lösen / umgehen kann, sondern warum ich die generischen Typparameter explizit angeben muss, wenn ich generische Methoden verkette. Oder anders gesagt, warum kann der Compiler die generischen Typparameter in einer Methodenkette nicht ableiten, insbesondere wenn die Methodenkette auf der rechten Seite einer Zuweisung ist? Wenn es möglich wäre, würde dies etwas anderes brechen (ich meine, in Bezug auf das Generika-System)?

BEARBEITEN :

Es gibt eine Frage, die dasselbe stellt , aber die einzige Antwort, die sie hat, erklärt nicht klar, warum der Compiler nicht funktioniert. t die generischen Typparameter in einer Methodenkette ableiten. Alles, was es hat, ist ein Verweis auf einen kleinen Absatz in den JSR-000335 Lambda-Ausdrücken für die endgültige JavaTM-Programmiersprache für die Evaluierung (Spezifikationsabschnitt D):

  

Es gab ein gewisses Interesse daran, Rückschlüsse auf "Ketten" zuzulassen: in a (). b (), die Typinformationen vom Aufruf von b zum Aufruf von a weiterleiten. Dies fügt der Komplexität des Inferenzalgorithmus eine weitere Dimension hinzu, da Teilinformationen in beiden Richtungen passieren müssen; es funktioniert nur, wenn das Löschen des Rückgabetyps von a () für alle Instanziierungen (z. B. Liste) festgelegt ist. Diese Funktion würde nicht sehr gut in das Poly-Ausdrucksmodell passen, da der Zieltyp nicht einfach abgeleitet werden kann. aber vielleicht mit zusätzlichen Verbesserungen könnte es in der Zukunft hinzugefügt werden.

Ich denke, ich könnte meine ursprüngliche Frage wie folgt umformulieren:

  • Was wären diese zusätzlichen Verbesserungen?
  • Wie kommt es, dass die Weitergabe von Teilinformationen in beide Richtungen den Inferenzalgorithmus komplexer machen würde?
  • Warum funktioniert das nur, wenn das Löschen des Rückgabetyps von a () für alle Instanziierungen (z. B. Liste) festgelegt ist? In der Tat, was bedeutet es, dass das Löschen des Rückgabetyps einer Methode für alle Instanziierungen feststeht?
  • Warum würde dieses Feature nicht sehr gut in das Poly-Expression-Modell passen? Was ist das Poly-Ausdrucksmodell? Und warum konnte der Zieltyp in diesem Fall nicht einfach abgeleitet werden?
Federico Peralta Schaffner 09.05.2017, 19:07
quelle

1 Antwort

2

Wenn dies wirklich ein Kommentar sein sollte, lassen Sie es mich bitte wissen und ich werde es in getrennten Kommentaren (es wird wahrscheinlich nicht in einen einzigen Kommentar passen).

Erstes poly expression ist eines, das verschiedene Typen in verschiedenen Kontexten haben kann. Sie deklarieren etwas in contextA es hat typeA ; eins in contextB und es hat typeB .

Was du mit deiner Kartenerstellung über ImmutableMap.of("a", "b") oder ImmutableMap.of(1,2) machst, ist so etwas. Um genauer zu sein, sagt Kapitel 15.2 in der JLS dass dies tatsächlich ein Ausdruck für Klasseninstanzerstellung ist Ausdruck.

Bisher haben wir festgestellt, dass A generic class instance creation is a poly expression . So kann die Instanzerstellung verschiedene Typen haben, die auf dem Kontext basieren, in dem sie verwendet werden (und dies offensichtlich geschieht).

Nun, in Ihrem Beispiel mit ImmutableMap.builder sind die Dinge nicht so schwer zu verstehen (wenn Sie zB builder und of verketten könnten), wird der Builder wie folgt deklariert:

%Vor%

und ImmutableMap.of gefällt das:

%Vor%

Beachten Sie, dass beide die gleichen generischen Typen K,V deklarieren. Dies wäre wahrscheinlich leicht von einer Methode zur anderen zu übertragen. Aber bedenken Sie den Fall, wenn Ihre Methoden ( let's say 10 methods ) jeweils unterschiedliche Grenzen der generischen Typen deklarieren, wie:

%Vor%

Und so weiter. Und Sie können sie verketten. Informationen über Typen müssen von left to right und von right to left für den Rückschluss auf die Arbeit übergeben werden, und soweit ich es beurteilen kann, wäre es sehr schwer zu tun (abgesehen davon, dass es sehr komplex ist).

Im Moment funktioniert das so, dass jeder Poly-Ausdruck einzeln aus dem, was ich sehe, kompiliert wird (und Ihre Beispiele scheinen das zu beweisen).

    
Eugene 09.05.2017, 21:47
quelle