Warum führt Java keine Inferenz durch?

44

Ich habe mich immer gefragt, warum Java keine Typinferenz ausführt, da die Sprache so ist, wie sie ist, und die VM sehr ausgereift ist. Googles Go ist ein Beispiel für eine Sprache mit hervorragenden Tippfehlern und reduziert den Aufwand für die Eingabe. Gibt es einen besonderen Grund dafür, dass diese Funktion nicht Teil von Java ist?

Cobie
quelle
3
Rückwärtskompatibilität Regeln in einer Sprache , wie alt und populär wie Java
Ratsche Freak
9
@ratchetfreak Konnte die Typinferenz nicht abwärtskompatibel hinzugefügt werden? Ältere Programme liefern lediglich mehr Typinformationen als erforderlich.
1
Bei Verwendung von Type Erasure kann es zu unerwünschten Effekten kommen. docs.oracle.com/javase/tutorial/java/generics/…
Zachary Yates
4
Beachten Sie, dass Java 8 mit seiner Lambda-Funktion eine Menge Schlüsse zieht: Sie können komplexe Lambdas schreiben, ohne irgendwelche Typen und alles, was abgeleitet wird, zu erwähnen.
Joachim Sauer
4
Inferenz lokaler Variablentypen kommt nach Java: JEP 286:
Jimmy Page

Antworten:

60

Aus technischer Sicht verfügt Java bei der Verwendung von Generika über eine Typinferenz . Mit einer generischen Methode wie

public <T> T foo(T t) {
  return t;
}

Der Compiler wird das analysieren und verstehen, wenn Sie schreiben

// String
foo("bar");
// Integer
foo(new Integer(42));

Für den ersten Aufruf wird eine Zeichenfolge und für den zweiten Aufruf eine Ganzzahl zurückgegeben, basierend auf dem, was als Argument eingegeben wurde. Als Ergebnis erhalten Sie die richtige Überprüfung zur Kompilierungszeit. Darüber hinaus kann man in Java 7 zusätzliche Typinferenzen erhalten, wenn Generika wie diese instanziiert werden

Map<String, String> foo = new HashMap<>();

Java ist so nett, die leeren spitzen Klammern für uns auszufüllen. Warum unterstützt Java keine Typinferenz als Teil der Variablenzuweisung? Zu einem bestimmten Zeitpunkt gab es eine RFE für die Typinferenzierung in Variablendeklarationen, die jedoch geschlossen wurde, weil "Nicht behoben"

Der Mensch profitiert auf zweierlei Weise von der Redundanz der Typdeklaration. Erstens dient der redundante Typ als wertvolle Dokumentation - Leser müssen nicht nach der Deklaration von getMap () suchen, um herauszufinden, welchen Typ er zurückgibt. Zweitens ermöglicht die Redundanz dem Programmierer, den beabsichtigten Typ zu deklarieren und dadurch von einer vom Compiler durchgeführten Gegenprüfung zu profitieren.

Der Beitragende, der dies schloss, bemerkte auch, dass es sich einfach "un-java-like" anfühlt, dem ich zustimmen kann. Javas Ausführlichkeit kann sowohl ein Segen als auch ein Fluch sein, aber sie macht die Sprache zu dem, was sie ist.

Natürlich war diese besondere RFE nicht das Ende dieser Unterhaltung. In Java 7 wurde diese Funktion erneut in Betracht gezogen , und einige Testimplementierungen wurden erstellt, darunter eine von James Gosling. Auch dieses Feature wurde letztendlich abgeschossen.

Mit der Veröffentlichung von Java 8 erhalten wir jetzt Typinferenz als Teil von Lambdas als solche:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

Der Java-Compiler kann die Methode Collections#sort(List<T>, Comparator<? super T>)und dann die Schnittstelle von Comparator#compare(T o1, T o2)und bestimmen, firstund secondsollte Stringes dem Programmierer daher ermöglichen, auf das erneute Eingeben des Typs im Lambda-Ausdruck zu verzichten.

Stich
quelle
5
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns- ja, wenn ja, HashMap<String, Integer> map = foo.getMap()stimme ich zu - in C # verwende ich varin solchen Fällen normalerweise nicht , obwohl ich könnte. Aber dieses Argument hält nicht Wasser, wenn es ist HashMap<String, Integer> map = new HashMap<String, Integer>(). Dies ist eine echte Redundanz, und ich sehe keinen Vorteil darin, den Typnamen zweimal schreiben zu müssen. Und wie ich hier von einem Compiler-Crosscheck profitieren würde, verstehe ich überhaupt nicht.
Konrad Morawski
9
Ja, und das ist gut für Generika, aber ich vermisse immer noch das Äquivalent von C # varin Java.
Konrad Morawski
15
Wie es in den Sinn kommt "un-java-like", das (käsig) Geschichte zu sein;) 9gag.com/gag/2308699/-this-is-how-things-are-done-around-here
Konrad Morawski
6
Außerdem ist der Rückgabetyp einer Funktion oft offensichtlich oder unnötig, und selbst in Fällen, in denen es nicht Ihre IDE ist, kann der Typ in einer halben Sekunde angezeigt werden.
Phoshi
8
Ich glaube, das offizielle Zitat Humans benefit from the redundancyist die wirkliche Antwort auf die aktuelle Frage, denn jede Begründung, die ich gelesen habe, scheint eine unbegründete und lächerliche Entschuldigung von Java-Designern zu sein: Sowohl C # als auch C ++ haben die Funktion, C # - und C ++ - Designer sind nicht weniger kompetent als Java ist, und jeder ist glücklich damit. Was würde dazu führen, dass sich Java-Entwickler von C # - oder C ++ - Entwicklern unterscheiden? Deshalb stimme ich @KonradMorawski zu: "So wird es hier gemacht" scheint wieder der wahre Grund dafür zu sein.
Paercebal
16

Zunächst einmal hat die Typinferenz nichts mit der Laufzeit zu tun, unabhängig davon, ob es sich bei dieser Laufzeit um eine 30 Jahre alte CPU oder eine VM handelt, die so neu ist, dass die Bits immer noch glänzend sind. Alles dreht sich um den Compiler.

Das heißt, es ist für Generika zulässig. Der Grund, warum es für nicht generische Typen nicht zulässig ist, scheint in der Philosophie zu liegen - nichts hindert die Designer daran, es hinzuzufügen.

Update: Offenbar wird es von Java 10 unterstützt - http://openjdk.java.net/jeps/286

jmoreno
quelle
5

Soweit ich weiß, war die Typinferenz zu Beginn der neunziger Jahre bei Java-Entwicklern unter den gängigen Sprachen nicht so beliebt (aber es war bereits ein sehr bekanntes Konzept, z. B. in ML). Daher kann ich mir vorstellen, dass Typinferenz wahrscheinlich nicht unterstützt wurde, da Java auf Programmierer abzielte, die aus C ++, Pascal oder anderen Mainstream-Sprachen stammten, die es nicht hatten (Prinzip der geringsten Überraschung).

Eines der Entwurfsprinzipien von Java besteht darin, Dinge explizit zu schreiben, um sicherzustellen, dass Programmierer und Compiler den Code gleich verstehen: Durch das Duplizieren von Informationen wird die Wahrscheinlichkeit von Fehlern verringert. Es mag natürlich eine Geschmackssache sein, ob die Eingabe einiger weiterer Zeichen die zusätzliche Sicherheit wert ist, aber dies war die Designphilosophie für Java: Schreiben Sie Dinge explizit.

Ich weiß nicht, ob Java in Zukunft eine Typinferenz erhalten wird, aber IMO, das wäre eine große Revolution für die Sprache (wie Glenn Nelson erwähnte, wurde es als "un-java-like" beschrieben), und dann könnte man auch in Betracht ziehen, die zu löschen benenne Java zugunsten eines neuen Namens.

Wenn Sie eine JVM-Sprache mit Typinferenz verwenden möchten, können Sie Scala verwenden.

Giorgio
quelle
2

Ich kann mir ein paar mögliche Gründe vorstellen. Zum einen ist die explizite Eingabe selbstdokumentierend. Java macht dies im Allgemeinen zu einer Priorität vor der Konkretisierung. Ein weiterer Grund könnte in Fällen liegen, in denen der Typ etwas mehrdeutig ist. Zum Beispiel, wenn ein Typ oder ein Subtyp eine Routine erfüllen könnte. Angenommen, Sie möchten eine Liste verwenden, aber jemand kommt und verwendet eine Methode, die nur für ArrayList gilt. Die JIT würde eine ArrayList ableiten und weitermachen, selbst wenn Sie einen Kompilierungsfehler wollten.

jiggy
quelle
8
Wie funktioniert GridBagLayout gridbag = new GridBagLayout (); zur Selbstdokumentation hinzufügen? Es ist reine Wiederholung.
Anders Lindén
3
Es unterscheidet sich von einem Fall, in dem die beiden Typen nicht gleich sind. Genauso einfach können Sie eine Instanz einer Unterklasse zuweisen.
jiggy
Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error- Ich verstehe das nicht. Die Typinferenz findet zum Zeitpunkt des Instatisierens einer Variablen statt, ohne dass eine Methode dafür aufgerufen wird. Könnten Sie vielleicht ein Beispiel dafür zeigen, was Sie gemeint haben?
Konrad Morawski
2
@KonradMorawski: Ich nehme an, er meint, wenn eine Methode ArrayList zurückgibt, wird der Typ darauf geschlossen. Wenn Sie einen Typ als etwas anderes als den Rückgabetyp behandeln möchten, können Sie keine Inferenz verwenden. Ich verstehe jedoch nicht, wie dies jemals ein Problem in einer Codebasis sein könnte, die sich dem Verstand nähert.
Phoshi
Die GEG würde bei der Typinferenz keine Rolle spielen. Typinferenz ist ein Phänomen zur Kompilierungszeit. Wenn eine Variable als Verweis auf eine Liste deklariert wird und versucht wird, auf ArrayList-Mitglieder zuzugreifen, wird check nicht eingegeben, und es tritt ein Kompilierungsfehler auf
sara
0

Dies widerspricht der gängigen Empfehlung, Variablen mit der allgemeinsten Schnittstelle zu deklarieren, die Ihren Anforderungen entspricht, und sie mit einer geeigneten Implementierungsklasse wie in zu initialisieren

Collection<String> names = new ArrayList<>();

Effektiv,

var names = new ArrayList<String>();

ist nichts anderes als syntaktischer Zucker für

ArrayList<String> names = new ArrayList<String>();

Wenn Sie das wollen, kann Ihre IDE es new ArrayList<String>()mit "einem Klick" aus dem Ausdruck erzeugen (refactor / create local variable), aber denken Sie daran, dass es der Empfehlung "use interfaces" widerspricht.

Ralf Kleberhoff
quelle