502 Stimmen

Was ist der Zweck des Diamant-Operators (<>) in Java?

Der Diamant-Operator in Java 7 ermöglicht Code wie folgt:

List list = new LinkedList<>();

In Java 5/6 kann ich jedoch einfach schreiben:

List list = new LinkedList();

Mein Verständnis von Typenlöschung ist, dass diese genau gleich sind. (Die Generika werden sowieso zur Laufzeit entfernt).

Warum überhaupt den Diamanten verwenden? Welche neue Funktionalität / Typensicherheit ermöglicht er? Wenn er keine neue Funktionalität liefert, warum erwähnen sie es dann als Feature? Ist mein Verständnis dieses Konzepts fehlerhaft?

4 Stimmen

Beachten Sie, dass dies kein Problem darstellt, wenn Sie statische Fabrikmethoden verwenden, da Java Typenschlüsseleinschätzung bei Methodenaufrufen durchführt.

0 Stimmen

Wenn Sie die Warnung deaktivieren, ist sie tatsächlich nutzlos... :) wie für mich

4 Stimmen

Es wurde beantwortet, aber es befindet sich auch im Java-Tutorial (etwa in der Mitte der Seite): docs.oracle.com/javase/tutorial/java/generics/…

548voto

ColinD Punkte 106101

Das Problem mit

List list = new LinkedList();

ist, dass auf der linken Seite der generische Typ List verwendet wird, wohingegen auf der rechten Seite der raw Typ LinkedList verwendet wird. Raw-Typen in Java existieren effektiv nur für die Kompatibilität mit Code vor Generics und sollten in neuem Code nie verwendet werden, es sei denn, es ist unbedingt erforderlich.

Angenommen, Java hätte von Anfang an Generics und hätte keine Typen wie LinkedList, die ursprünglich erstellt wurden, bevor es Generics hatte, dann hätte es wahrscheinlich so gemacht, dass der Konstruktor eines generischen Typs automatisch seine Typparameter von der linken Seite der Zuweisung ableitet, wenn möglich. Aber das ist nicht der Fall, und es muss Raw-Typen und generische Typen aus Gründen der Abwärtskompatibilität unterschiedlich behandeln. Das führt dazu, dass sie eine etwas andere, aber gleichzeitig bequeme Methode zum Deklarieren einer neuen Instanz eines generischen Objekts benötigen, ohne die Typparameter erneut angeben zu müssen... den Diamantenoperator.

In Bezug auf Ihr ursprüngliches Beispiel von List list = new LinkedList() erzeugt der Compiler eine Warnung für diese Zuweisung, weil er muss. Betrachten Sie dies:

List strings = ... // einige Liste, die einige Zeichenketten enthält

// Völlig legal, da Sie den Raw-Typ verwendet haben und jegliche Typüberprüfung verloren haben!
List integers = new LinkedList(strings);

Generics dienen dazu, einen Schutz zur Compile-Zeit gegen falsche Anwendungen zu bieten. Im obigen Beispiel bedeutet die Verwendung des Raw-Typs, dass Sie diesen Schutz nicht erhalten und bei der Ausführung einen Fehler erhalten werden. Deshalb sollten Sie keine Raw-Typen verwenden.

// Nicht zulässig, da die rechte Seite tatsächlich generisch ist!
List integers = new LinkedList<>(strings);

Der Diamantenoperator ermöglicht es jedoch, dass die rechte Seite der Zuweisung als echte generische Instanz mit denselben Typparametern wie die linke Seite definiert werden kann... ohne diese Parameter erneut eingeben zu müssen. Er ermöglicht es Ihnen, die Sicherheit von Generics mit fast demselben Aufwand wie bei der Verwendung des Raw-Typs beizubehalten.

Ich denke, das Entscheidende zu verstehen ist, dass Raw-Typen (ohne <>) nicht genauso behandelt werden können wie generische Typen. Wenn Sie einen Raw-Typ deklarieren, erhalten Sie keinerlei Vorteile und Typüberprüfungen von Generics. Sie müssen auch bedenken, dass Generics ein allgemeiner Bestandteil der Java-Sprache sind... sie gelten nicht nur für die no-arg-Konstruktoren von Collectionn!

0 Stimmen

Im zweiten Codeblock sollte diese Zeile LinkedList raw = a; eine unbeaufsichtigte Warnung verursachen. Es entspricht nicht dem im Frage gestellten Ansatz.

0 Stimmen

@Roman: Es ist dasselbe in dem Sinne, dass die rechte Seite in beiden Fällen ein Rohdatentyp ist. Rohdatentypen und generische Typen sollten nicht gemischt werden, das ist alles.

0 Stimmen

@Roman: Ich habe das Beispiel geändert, um deutlicher zu zeigen, dass es auch dann Problempotenziale gibt, wenn die rechte Seite new LinkedList ist.

40voto

Octavian A. Damiean Punkte 39044

Dein Verständnis ist leicht fehlerhaft. Der Diamantoperator ist eine nette Funktion, da du dich nicht wiederholen musst. Es ergibt Sinn, den Typ einmal zu definieren, wenn du den Typ deklarierst, aber es ergibt einfach keinen Sinn, ihn erneut auf der rechten Seite zu definieren. Das DRY-Prinzip.

Jetzt um den ganzen Wirbel um die Definition von Typen zu erklären. Du hast Recht, dass der Typ zur Laufzeit entfernt wird, aber sobald du etwas aus einer Liste mit Typdefinition abrufen möchtest, erhältst du es zurück als den Typ, den du definiert hast, als du die Liste deklariert hast, sonst würde sie alle spezifischen Funktionen verlieren und nur die Objektfunktionen haben, außer wenn du das abgerufene Objekt in seinen ursprünglichen Typ umwandelst, was manchmal sehr knifflig sein kann und zu einer ClassCastException führen kann.

Die Verwendung von List list = new LinkedList() wird dir Warnungen wegen Rawtypes bringen.

8 Stimmen

List list = new LinkedList() ist der richtige Code. Du kennst das und ich kenne das auch. Und die Frage (so wie ich sie verstehe) lautet: warum versteht nur der Java-Compiler nicht, dass dieser Code ziemlich sicher ist?

25 Stimmen

@Roman: List liste = new LinkedList() ist nicht der richtige Code. Sicher, es wäre schön, wenn es so wäre! Und wahrscheinlich könnte es so gewesen sein, wenn Java von Anfang an Generics gehabt hätte und nicht mit der Abwärtskompatibilität von generischen Typen umgehen müsste, die früher nicht generisch waren, aber das tut es.

7 Stimmen

@ColinD Java muss wirklich nicht jede einzelne Zeile rückwärtskompatibel behandeln. In jeder Java-Quellcodedatei mit Generics sollten die alten nicht-generischen Typen verboten sein (Sie können immer verwenden, wenn Sie mit Legacy-Code interagieren) und der nutzlose Diamantoperator sollte nicht existieren.

15voto

Roman Punkte 61632

Diese Zeile verursacht die [unchecked] Warnung:

List list = new LinkedList();

Also lautet die Frage: Warum wird die [unchecked] Warnung nicht automatisch unterdrückt, nur für den Fall, dass eine neue Sammlung erstellt wird?

Ich denke, es wäre eine viel schwierigere Aufgabe, als das <> Feature hinzuzufügen.

UPD: Ich denke auch, dass es ein Durcheinander gäbe, wenn es rechtens wäre, Roh-Typen "nur für einige Dinge" zu verwenden.

12voto

maaartinus Punkte 42477

Im Prinzip ermöglicht es der Diamant-Operator, kompakteren (und lesbaren) Code zu schreiben, indem wiederholte Typargumente gespart werden. In der Praxis sind es jedoch nur zwei verwirrende Zeichen mehr, die dir nichts bringen. Warum?

  1. Kein vernünftiger Programmierer verwendet Raw-Typen in neuem Code. Der Compiler könnte also einfach davon ausgehen, dass du durch das Schreiben von keinen Typargumenten möchtest, dass er sie ableitet.
  2. Der Diamant-Operator liefert keine Typinformationen, er sagt nur dem Compiler: "Das wird schon gut gehen". Indem du ihn weglässt, kannst du keinen Schaden anrichten. An jeder Stelle, an der der Diamant-Operator erlaubt ist, könnte er vom Compiler "abgeleitet" werden.

Meiner Meinung nach wäre es nützlicher, einen klaren und einfachen Weg zu haben, um eine Quelle als Java 7 zu kennzeichnen, als solche seltsamen Dinge zu erfinden. In so gekennzeichnetem Code könnten Raw-Typen verboten werden, ohne etwas zu verlieren.

Übrigens, ich denke nicht, dass dies per Kompilierungsschalter erfolgen sollte. Die Java-Version einer Programmdatei ist ein Attribut der Datei, keine Option. Etwas so Triviales wie

package 7 com.example;

würde es klar machen (du kannst auch etwas anspruchsvolleres bevorzugen, das ein oder mehrere ausgefallene Schlüsselwörter einschließt). Es würde sogar ermöglichen, Quellen für verschiedene Java-Versionen gemeinsam zu kompilieren, ohne Probleme zu haben. Es würde es erlauben, neue Schlüsselwörter (z.B. "Modul") einzuführen oder veraltete Features zu streichen (mehrere nicht-öffentliche nicht-verschachtelte Klassen in einer einzigen Datei oder Ähnliches) ohne jegliche Inkompatibilität zu verursachen.

2 Stimmen

Hast du den Unterschied zwischen new ArrayList(anotherList) und new ArrayList<>(anotherList) in Betracht gezogen (vor allem wenn es zu List zuweisen wird und anotherList eine List ist)?

0 Stimmen

@Paul Bellora: Nein. Überraschenderweise kompilieren beide. Selbst die mit Diamanten, ohne Warnung. Allerdings kann ich keinen Sinn darin erkennen, kannst du das näher erläutern?

0 Stimmen

Entschuldigung, ich habe mich nicht sehr gut erklärt. Sehen Sie den Unterschied zwischen diesen beiden Beispielen: ideone.com/uyHagh und ideone.com/ANkg3T Ich möchte nur darauf hinweisen, dass es wichtig ist, den Diamantoperator anstelle eines Rohdatentyps zu verwenden, zumindest wenn Argumente mit generischen Grenzen übergeben werden.

8voto

axtavt Punkte 233070

Wenn Sie List list = new LinkedList(); schreiben, erzeugt der Compiler eine "unchecked" Warnung. Sie können sie ignorieren, aber wenn Sie dazu neigen, diese Warnungen zu ignorieren, könnten Sie auch eine Warnung verpassen, die Sie über ein echtes Typsicherheitsproblem informiert.

Also ist es besser, einen Code zu schreiben, der keine zusätzlichen Warnungen erzeugt, und der Diamond Operator ermöglicht es Ihnen, dies auf bequeme Weise ohne unnötige Wiederholung zu tun.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X