Warum sollte Java keine generischen Typdeklarationen für eine Variablendeklaration zulassen?

8

Angenommen, wir haben eine Klasse wie diese:

%Vor%

Es wird nicht kompiliert, obwohl es intuitiv scheint, als ob es "sollte":

%Vor%

Der Grund dafür ist, dass foo keinen gebundenen generischen Typ hat, daher weiß der Compiler nicht, dass die Ausgabe von foo.getValue() mit der Eingabe von foo.setValue() kompatibel ist.

Um das zu beheben, müssen Sie eine neue Methode erstellen, nur um den generischen Typparameter in der for() Schleife zu binden:

%Vor%

Das hat mich immer geärgert. Außerdem scheint es eine einfache Lösung zu geben.

Meine Frage: Gibt es einen "guten" Grund, warum die Java-Sprache nicht erweitert werden sollte, um generische Typdeklarationen für Variablendeklarationen zuzulassen? Beispiel:

%Vor%

oder, genauer gesagt, in diesem Fall einer for() -Schleife:

%Vor%

Ich frage mich, ob ein Compiler-Assistent erklären kann, warum das entweder zu schwierig wäre oder (und sollte).

BEARBEITEN:

Als Antwort auf diese vorgeschlagene Lösung:

%Vor%

Diese Methodensignatur lässt Foo s mit verschiedenen generischen Typen nicht zusammen zurücksetzen. Mit anderen Worten, der Versuch, ein Iterable<Foo<?>> zu übergeben, verursacht einen Kompilierungsfehler.

In diesem Beispiel wird das Problem veranschaulicht:

%Vor%

Der Kompilierfehler lautet:

  

Methode resetFoos kann nicht auf bestimmte Typen angewendet werden;

     

erforderlich: Iterable<Foo<T>>

     

gefunden: List<Foo<?>>

     

Grund: Keine Instanz (en) vom Typ Variable (n) T existieren, so dass der Argumenttyp List<Foo<?>> dem formalen Parametertyp Iterable<Foo<T>> entspricht.     wo T eine Typvariable ist:        T extends Object deklariert in der Methode <T>resetFoos(Iterable<Foo<T>>)

(mit sun-jdk-1.7.0_10 über ideone.com)

    
Archie 08.04.2013, 19:29
quelle

5 Antworten

2

Grundsätzlich können Typvariablen in Java nur zwei Bereiche haben: 1) Klassenbereich oder 2) Methodenbereich. Sie fragen, warum nicht einen anderen Bereich erlauben - Bereich eines lokalen Code-Block (in diesem Fall das Innere einer for-Schleife.

Ja, in manchen Fällen wäre das hilfreich. Und es wäre nicht schwer, es der Sprache hinzuzufügen. Dies sind jedoch ziemlich seltene Fälle, und es ist wahrscheinlicher, Menschen zu verwirren als zu helfen. Es gibt auch eine relativ einfache und effektive Problemumgehung, die Sie, wie Sie bereits festgestellt haben, in den lokalen Blockbereich auf eine private Hilfsfunktion verschieben, die dann eine Variable vom Typ des Methodenbereichs verwenden kann:

%Vor%

Ja, dies könnte den Code durch zusätzliche Methodenaufrufe weniger effizient machen, aber das ist ein geringes Problem.

    
newacct 09.04.2013, 18:23
quelle
2

Sie können die Methode resetFoos selbst generisch machen:

%Vor%

Auf diese Weise weiß der Compiler, dass die T von foo.getValue() die gleiche T für foo.setValue() ist.

Das ist nur die Problemumgehung. Ich weiß nicht, warum der Compiler nicht erlaubt, Generics auf einer Variablenebene wie final <T> Foo<T> typedFoo = foo; ; Ich weiß nur, dass Sie es nicht auf der Variablenebene deklarieren können. Hier ist jedoch die Methodenstufe ausreichend.

    
rgettman 08.04.2013 19:31
quelle
2

Es könnte wahrscheinlich gemacht werden, möglicherweise mit einigen Randfällen. Aber wirklich, der Grund, warum es nicht gemacht wurde, ist einfach, dass die JLS-Leute damit aufhörten.

Ein Typsystem kann nicht alles über Ihr Programm ableiten. Es wird eine Menge tun, aber es wird immer einen Platz geben, an dem ein Compiler / Typ-Checker nichts weiß, was der Mensch beweisen kann. Also müssen die Leute, die Compiler und Typ-Checker entwerfen, entscheiden, wie weit sie gehen sollen, wenn es darum geht, zu folgern und clever zu sein - und egal, wo sie aufhören, wird jemand immer eine Frage wie diese stellen können: "Warum didn '?" Gehen sie diesen zusätzlichen Schritt? "

Meine Vermutung - und es ist nur eine Vermutung - ist, dass diese Schlussfolgerung es nicht geschafft hat wegen einer Mischung aus (a) es ist leicht zu umgehen (wie rgettman zeigt), (b) es kompliziert die Sprache, (b.1) es führt knifflige Randfälle ein, von denen einige sogar mit anderen Merkmalen der Sprache unvereinbar sind, (c) die Designer der JLS hatten nicht das Gefühl, dass sie Zeit hatten, daran zu arbeiten.

    
yshavit 08.04.2013 19:37
quelle
2

Jede Änderung der Java-Sprache wird sehr schwierig sein.

In Ihrem Beispiel geht es nicht wirklich um die Notwendigkeit einer Typvariablen. Der Compiler erstellt bereits eine Typvariable durch Wildcard-Capture, nur dass die Typvariable für den Programmierer nicht verfügbar ist. Es könnte mehrere Mittel geben

  1. Lassen Sie den Programmierer auf die Typvariable zugreifen. Dies ist keine leichte Aufgabe. Wir müssen eine bizzare Syntax und Semantik erfinden. Die Seltsamkeit und Komplexität mag den Nutzen nicht rechtfertigen.

  2. Gemeinsame Capture-Konvertierung Momentan wird jeder Ausdruck separat durch Capture Conversion abgearbeitet. Die Spezifikation erkennt nicht, dass foo foo ist, sodass die beiden Ausdrücke dieselbe Capture-Konvertierung verwenden sollten. Es ist in einigen einfachen Fällen machbar, zum Beispiel sollte eine effektive finale lokale Variable nur einmal und nicht bei jedem Zugriff Capture-konvertiert werden.

  3. abgeleiteter Variablentyp - var foo2 = foo; Der Typ von foo2 wird von der rechten Seite abgeleitet, die zuerst die Capture-Konvertierung durchläuft, daher ist der Typ foo2 Foo<x> for some x , mit einem neuen Typ Variable x (immer noch unbenennbar). Oder verwende var direkt auf foo - for(var foo: foos) . Der abgeleitete Variablentyp ist eine ausgereifte / getestete Technik, und sein Nutzen ist viel breiter als die Lösung dieses Problems. Also werde ich für diesen wählen. Es könnte in Java 9 kommen, wenn wir so lange leben können.

ZhongYu 08.04.2013 21:04
quelle
1

Das Problem ist, dass in der fraglichen Aussage:

%Vor%

Die zwei Vorkommen von ? können (soweit der Compiler weiß) an verschiedene Typen gebunden sein. Es mag albern erscheinen, dass der Compiler nicht herausfinden kann, dass ein ? (das von getValue() zurückgegeben wird) nicht dasselbe ist wie das ? , das in setValue erwartet wird. Aber in anderen Situationen ist das Ergebnis nicht so eindeutig. Die Sprache erfordert, dass Typparameter gleich benannt werden, wenn sie identisch sein sollen.

Ein guter Workaround ist in der Antwort von rgettman enthalten.

    
Ted Hopp 08.04.2013 19:38
quelle

Tags und Links