Warum funktioniert die Verwendung von Collections.emptySet() mit Generika in der Zuweisung, aber nicht als Methodenparameter?

Lesezeit: 4 Minuten

Benutzer-Avatar
Karl von L

Also, ich habe eine Klasse mit einem Konstruktor wie diesem:

public FilterList(Set<Integer> labels) {
    ...
}

und ich möchte einen neuen bauen FilterList Objekt mit einer leeren Menge. Dem Rat von Joshua Bloch in seinem Buch Effective Java folgend, möchte ich kein neues Objekt für die leere Menge erstellen; Ich werde nur verwenden Collections.emptySet() stattdessen:

FilterList emptyList = new FilterList(Collections.emptySet());

Dies gibt mir einen Fehler, der sich darüber beschwert java.util.Set<java.lang.Object> ist kein java.util.Set<java.lang.Integer>. Okay, wie wäre es damit:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet());

Dies gibt mir auch einen Fehler! Okay, wie wäre es damit:

Set<Integer> empty = Collections.emptySet();
FilterList emptyList = new FilterList(empty);

He, es funktioniert! Aber wieso? Schließlich hat Java keinen Typrückschluss, weshalb Sie in diesem Fall eine Warnung vor einer ungeprüften Konvertierung erhalten Set<Integer> foo = new TreeSet() Anstatt von Set<Integer> foo = new TreeSet<Integer>(). Aber Set<Integer> empty = Collections.emptySet(); funktioniert auch ohne Vorwarnung. Warum ist das so?

  • Alle folgenden Antworten sind richtig, aber was ich nicht verstehe, ist: Warum würden Sie Ihre Sammlung mit einer leeren Liste initialisieren, anstatt einen Standardkonstruktor ohne Parameter aufzurufen? Jede Sammlung, die ich kenne, hat einen Konstruktor mit einer vorhandenen Sammlung und einen leeren Konstruktor.

    – Sean Patrick Floyd

    17. Juni 2010 um 13:35 Uhr

  • Interessanterweise wird Folgendes kompiliert: FilterList emptyList = new FilterList((Set<Integer>)(Set<? extends Object>)Collections.emptySet());

    – EricS

    13. September 2012 um 18:43 Uhr


  • Noch ein ziemlich lustiges Beispiel: Set<Integer> emptySet = (Set<Integer>)Collections.emptySet(); kompiliert nicht.

    – neo

    27. November 2013 um 18:05 Uhr

Die kurze Antwort lautet: Das ist eine Einschränkung der Typinferenz im generischen System von Java. Es kann generische Typen von konkreten Variablen ableiten, aber nicht von Methodenparametern.

ich vermuten Dies liegt daran, dass Methoden abhängig von der Laufzeitklasse des besitzenden Objekts dynamisch abgesetzt werden, also zur Kompilierzeit (when alle allgemeine Informationen aufgelöst werden) können Sie nicht sicher wissen, welche Klasse der Methodenparameter sein wird, und können daher nicht ableiten. Variablendeklarationen sind nett und konstant, also können Sie.

Vielleicht kann jemand anderes mehr Details und / oder einen netten Link geben. 🙂

In jedem Fall können Sie die Typparameter immer explizit für generische Aufrufe wie folgt angeben:

Collections.<Integer>emptySet();

oder sogar mehrere Parameter auf einmal, zB

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean>

Dies sieht oft ein wenig sauberer aus, als in Fällen, in denen die Inferenz nicht greift, zu casten.

  • +1 für eine gute Erklärung des Warum. Ich wünschte, man könnte in SO zwei Antworten richtig markieren 🙂

    – jdmichal

    17. Juni 2010 um 13:10 Uhr

  • Ich weiß, dass der Compiler beim Ableiten den Methodenaufruf nicht auf Kontext prüft, aber ich bin mir nicht sicher, warum. Zumindest für Privat bzw final Methoden sollte es möglich sein, Rückschlüsse zu ziehen.

    – Hank Gay

    17. Juni 2010 um 13:23 Uhr

  • @ Hank: oder static Methoden, die ebenfalls zur Kompilierzeit aufgelöst werden. Trotzdem – es nichtwas meiner Meinung nach der Hauptpunkt ist.

    – Andrzej Doyle

    17. Juni 2010 um 13:25 Uhr

  • „Ich vermute, das liegt daran, dass Methoden abhängig von der Laufzeitklasse des besitzenden Objekts dynamisch abgesetzt werden, sodass Sie zur Kompilierzeit nicht genau wissen können, welche Klasse der Methodenparameter sein wird, und daher nicht ableiten können .” Warum funktioniert dann die Version mit dem Typcast nicht?

    – EricS

    13. September 2012 um 18:39 Uhr


  • “Ich vermute, das liegt daran, dass Methoden abhängig von der Laufzeitklasse des besitzenden Objekts dynamisch versendet werden.” Ich bin mir nicht sicher, ob es dasselbe ist, aber welche Version einer überladenen Methode aufgerufen werden soll, wird zur Kompilierzeit bestimmt: kaufmännisches Und.space/blog/2007/1/12/java-overload

    – Kipp

    16. Mai 2016 um 14:57 Uhr


Benutzer-Avatar
Andrej Fierbinteanu

Versuchen

FilterList emptyList = new FilterList(Collections.<Integer>emptySet());

Sie können den Typparameter für Methoden erzwingen, die ihn haben, in Fällen, in denen die Inferenz nicht gut genug ist, oder um Ihnen die Verwendung von Untertypen zu ermöglichen. zum Beispiel:

// forces use of ArrayList as parameter instead of the infered List
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType();

Benutzer-Avatar
jdmichal

Sie möchten dies tun:

FilterList emptyList = new FilterList(java.util.Collections.<Integer>emptySet());

Das sagt die emptySet -Methode, die ihr generischer Parameter explizit verwenden sollte Integer statt der Vorgabe Object. Und ja, die Syntax ist dafür völlig irre und nicht intuitiv. 🙂

  • Diese Syntax war mir nicht bekannt, danke! Aber ich frage mich immer noch, warum diese Syntax in der Variablenzuweisung nicht notwendig ist.

    – Karl von L

    17. Juni 2010 um 13:10 Uhr

  • Siehe die Antwort von Andrzej Doyle. Ich glaube, es ist eine gute Erklärung.

    – jdmichal

    17. Juni 2010 um 13:12 Uhr

  • Du brauchst nicht new dort. Tatsächlich glaube ich nicht, dass es damit kompilieren wird.

    – Andrej Fierbinteanu

    17. Juni 2010 um 13:29 Uhr

Java hat einen Typrückschluss, es ist nur ziemlich begrenzt. Wenn Sie genau wissen möchten, wie es funktioniert und wo seine Grenzen liegen, ist dies eine wirklich gute Lektüre:

http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#Type%2BArgument%2BInference

1380670cookie-checkWarum funktioniert die Verwendung von Collections.emptySet() mit Generika in der Zuweisung, aber nicht als Methodenparameter?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy