Der Diamantoperator in Java 7 erlaubt Code wie den folgenden:
List<String> list = new LinkedList<>();
In Java 5/6 kann ich jedoch einfach schreiben:
List<String> list = new LinkedList();
Mein Verständnis von Typlöschung ist, dass diese genau gleich sind. (Das Generikum wird sowieso zur Laufzeit entfernt).
Warum sich überhaupt mit dem Diamanten beschäftigen? Welche neue Funktionalität / Typensicherheit erlaubt es? Wenn es keine neuen Funktionen bietet, warum wird es dann als Feature erwähnt? Ist mein Verständnis dieses Konzepts fehlerhaft?
java
generics
java-7
diamond-operator
tofarr
quelle
quelle
Antworten:
Das Problem mit
ist, dass Sie auf der linken Seite den generischen Typ verwenden,
List<String>
während Sie auf der rechten Seite den Rohtyp verwendenLinkedList
. Raw-Typen in Java existieren effektiv nur aus Gründen der Kompatibilität mit vorgenerischem Code und sollten niemals in neuem Code verwendet werden, es sei denn, Sie müssen dies unbedingt tun.Wenn Java von Anfang an Generika hatte und keine Typen hatte, wie sie
LinkedList
ursprünglich erstellt wurden, bevor es Generika hatte, hätte es wahrscheinlich so gemacht werden können, dass der Konstruktor für einen generischen Typ seine Typparameter automatisch von links ableitet -hand Seite der Aufgabe, wenn möglich. Dies ist jedoch nicht der Fall, und aus Gründen der Abwärtskompatibilität müssen Rohtypen und generische Typen unterschiedlich behandelt werden. Das lässt sie etwas anders machen müssen , aber ebenso bequeme Methode zum Deklarieren einer neuen Instanz eines generischen Objekts festlegen, ohne die Typparameter wiederholen zu müssen ... den Diamantoperator.In Ihrem ursprünglichen Beispiel
List<String> list = new LinkedList()
generiert der Compiler eine Warnung für diese Zuweisung, da dies erforderlich ist. Bedenken Sie:Es gibt Generika, die während der Kompilierung Schutz vor Falschem bieten. Im obigen Beispiel bedeutet die Verwendung des Rohtyps, dass Sie diesen Schutz nicht erhalten und zur Laufzeit einen Fehler erhalten. Aus diesem Grund sollten Sie keine Rohtypen verwenden.
Der Diamantoperator ermöglicht jedoch, dass die rechte Seite der Zuweisung als echte generische Instanz mit denselben Typparametern wie die linke Seite definiert wird ... ohne dass diese Parameter erneut eingegeben werden müssen. Es ermöglicht Ihnen, die Sicherheit von Generika mit fast zu halten dem gleichen Aufwand wie bei der Verwendung .
Ich denke, der Schlüssel zum Verständnis ist, dass Rohtypen (ohne
<>
) nicht wie generische Typen behandelt werden können. Wenn Sie einen Rohtyp deklarieren, erhalten Sie keinen der Vorteile und die Typprüfung von Generika. Sie müssen auch bedenken, dass Generika ein allgemeiner Bestandteil der Java-Sprache sind ... sie gelten nicht nur für die No-Arg-Konstruktoren vonCollection
s!quelle
-compatibility
Compiler-Switch einführen , während bei Abwesenheitjavac
alle Raw-Typen verboten und nur streng generische Typen erzwungen werden? Dadurch werden unsere Codes weniger ausführlich.List<String> strings = new List<>()
ist in Ordnung, aber wenn Sie definierenprivate List<String> my list;
und dann die Hälfte der Seite, mit der Sie instanziierenmy_list = new List<>()
, dann ist es nicht cool! Was enthält meine Liste nochmal? Oh, lass mich nach der Definition suchen. Plötzlich verabschiedet sich der Vorteil der Diamantverknüpfung.my_list = getTheList()
? Es gibt mehrere bessere Möglichkeiten, um mit dieser Art von Problemen umzugehen: 1. Verwenden Sie eine IDE, die Ihnen beim Bewegen des Mauszeigers die Variablentypen anzeigt. 2. Verwenden Sie aussagekräftigere Variablennamen, z. B.private List<String> strings
3. Teilen Sie die Deklaration und Initialisierung von Variablen nicht auf, es sei denn, Sie müssen dies wirklich tun.Ihr Verständnis ist leicht fehlerhaft. Der Diamantoperator ist eine nette Funktion, da Sie sich nicht wiederholen müssen. Es ist sinnvoll, den Typ einmal zu definieren, wenn Sie den Typ deklarieren, aber es ist einfach nicht sinnvoll, ihn auf der rechten Seite erneut zu definieren. Das DRY-Prinzip.
Erklären Sie nun alle Probleme beim Definieren von Typen. Sie haben Recht, dass der Typ zur Laufzeit entfernt wird, aber sobald Sie etwas aus einer Liste mit Typdefinition abrufen möchten, erhalten Sie es als den Typ zurück, den Sie beim Deklarieren der Liste definiert haben, da sonst alle spezifischen Funktionen verloren gehen und nur die Objektfunktionen, außer wenn Sie das abgerufene Objekt in den ursprünglichen Typ umwandeln, was manchmal sehr schwierig sein kann und zu einer ClassCastException führt.
Wenn Sie verwenden
List<String> list = new LinkedList()
, erhalten Sie Warnungen vom Rohtyp.quelle
List<String> list = new LinkedList()
ist der richtige Code. Du weißt das und ich weiß das auch. Und die Frage (so wie ich es verstehe) ist: Warum versteht nur der Java-Compiler nicht, dass dieser Code ziemlich sicher ist?List<String> list = new LinkedList()
ist nicht der richtige Code. Klar, es wäre schön, wenn es so wäre! Und es hätte wahrscheinlich sein können, wenn Java von Anfang an Generika hatte und sich nicht mit der Abwärtskompatibilität von generischen Typen befassen musste, die früher nicht generisch waren, aber das tut es.<?>
wenn Sie eine Schnittstelle zu Legacy-Code herstellen), und der nutzlose Diamantoperator sollte nicht vorhanden sein.Diese Zeile verursacht die Warnung [nicht markiert]:
Die Frage ändert sich also: Warum wird die [nicht aktivierte] Warnung nicht automatisch nur für den Fall unterdrückt, dass eine neue Sammlung erstellt wird?
Ich denke, es wäre viel schwieriger, eine
<>
Funktion hinzuzufügen .UPD : Ich denke auch, dass es ein Chaos geben würde, wenn es legal wäre, rohe Typen "nur für ein paar Dinge" zu verwenden.
quelle
Theoretisch können Sie mit dem Diamantoperator kompakteren (und lesbareren) Code schreiben, indem Sie Argumente vom Typ "wiederholt" speichern. In der Praxis sind es nur zwei verwirrende Zeichen, die Ihnen nichts geben. Warum?
Meiner Meinung nach wäre es nützlicher, eine Quelle klar und einfach als Java 7 zu markieren, als solche seltsamen Dinge zu erfinden. In so markiertem Code könnten Rohtypen verboten werden, ohne etwas zu verlieren.
Übrigens denke ich nicht, dass dies mit einem Kompilierungsschalter erfolgen sollte. Die Java-Version einer Programmdatei ist ein Attribut der Datei, überhaupt keine Option. Verwenden Sie etwas so Triviales wie
könnte es klar machen (Sie bevorzugen vielleicht etwas Anspruchsvolleres, einschließlich eines oder mehrerer ausgefallener Schlüsselwörter). Es würde sogar ermöglichen, Quellen, die für verschiedene Java-Versionen geschrieben wurden, problemlos zusammen zu kompilieren. Es würde die Einführung neuer Schlüsselwörter (z. B. "Modul") oder das Löschen einiger veralteter Funktionen (mehrere nicht öffentliche nicht verschachtelte Klassen in einer einzelnen Datei oder was auch immer) ermöglichen, ohne die Kompatibilität zu verlieren.
quelle
new ArrayList(anotherList)
und berücksichtigtnew ArrayList<>(anotherList)
(insbesondere, wenn es a zugewiesen istList<String>
und aanotherList
istList<Integer>
)?new @RawType List()
. Das ist bereits eine gültige Java 8-Syntax, und Typanmerkungen ermöglichen die Verwendung an jedem Ort, an dem sie benötigt werden, z@RawType List = (@RawType List) genericMethod();
. In Anbetracht der Tatsache, dass Raw-Typen derzeit eine Compiler-Warnung erstellen, sofern keine entsprechende@SuppressWarnings
platziert wurde,@RawType
wäre dies ein angemessener Ersatz, und eine subtilere Syntax ist nicht erforderlich.Wenn du schreibst
List<String> list = new LinkedList();
, erzeugt der Compiler eine "ungeprüfte" Warnung. Sie können es ignorieren, aber wenn Sie diese Warnungen früher ignoriert haben, können Sie auch eine Warnung übersehen, die Sie über ein echtes Sicherheitsproblem informiert.Es ist daher besser, einen Code zu schreiben, der keine zusätzlichen Warnungen generiert, und mit dem Diamantoperator können Sie dies auf bequeme Weise ohne unnötige Wiederholung tun.
quelle
Alle in den anderen Antworten genannten sind gültig, aber die Anwendungsfälle sind meiner Meinung nach nicht vollständig gültig. Wenn man Guava und insbesondere die sammlungsbezogenen Dinge auscheckt, wurde dasselbe mit statischen Methoden gemacht. ZB Lists.newArrayList () , mit dem Sie schreiben können
oder mit statischem Import
Guave hat andere sehr mächtige Funktionen wie diese und ich kann mir eigentlich nicht viele Verwendungsmöglichkeiten für das <> vorstellen.
Es wäre nützlicher gewesen, wenn das Verhalten des Diamantoperators als Standard festgelegt worden wäre, dh der Typ wird von der linken Seite des Ausdrucks abgeleitet, oder wenn der Typ der linken Seite von der rechten Seite abgeleitet wurde. Letzteres passiert in Scala.
quelle
Der Punkt für den Diamantoperator besteht einfach darin, die Eingabe von Code beim Deklarieren generischer Typen zu reduzieren. Es hat keinerlei Auswirkungen auf die Laufzeit.
Der einzige Unterschied, wenn Sie in Java 5 und 6 angeben,
ist , dass Sie angeben müssen ,
@SuppressWarnings("unchecked")
an dielist
(sonst erhalten Sie eine ungeprüfte Guss Warnung). Mein Verständnis ist, dass der Diamantbetreiber versucht, die Entwicklung zu vereinfachen. Es hat überhaupt nichts mit der Laufzeitausführung von Generika zu tun.quelle