2008-11-20 9 views
227

Ich habe Probleme beim Navigieren der Java-Regel zum Ableiten generischer Typparameter. Betrachten Sie die folgende Klasse, die eine optionale Liste Parameter hat:Collections.emptyList() gibt eine Liste <Object> zurück?

Person.java:9: The constructor Person(String, List<Object>) is undefined 

Aber Collections.emptyList() kehrt Typ <T> List<T>, nicht List<Object>:

import java.util.Collections; 
import java.util.List; 

public class Person { 
    private String name; 
    private List<String> nicknames; 

    public Person(String name) { 
    this(name,Collections.emptyList()); 
    } 

    public Person(String name,List<String> nicknames) { 
    this.name = name; 
    this.nicknames = nicknames; 
    } 
} 

Mein Java-Compiler den folgenden Fehler gibt. Hinzufügen einer Guss nicht

public Person(String name) { 
    this(name,(List<String>)Collections.emptyList()); 
} 

hilft ergibt

Person.java:9: inconvertible types 

Mit EMPTY_LIST statt emptyList()

public Person(String name) { 
    this(name,Collections.EMPTY_LIST); 
} 

Erträge

Person.java:9: warning: [unchecked] unchecked conversion 

Während die folgende Änderung mak es geht der Fehler weg:

public Person(String name) { 
    this.name = name; 
    this.nicknames = Collections.emptyList(); 
} 

Kann mir jemand erklären, welche Art-Prüfregel Ich renne gegen hier, und der beste Weg, um es zu arbeiten? In diesem Beispiel ist das abschließende Codebeispiel zufriedenstellend, aber bei größeren Klassen würde ich gerne in der Lage sein, Methoden zu schreiben, die diesem "optionalen Parameter" -Muster folgen, ohne Code zu duplizieren.

Für zusätzliches Guthaben: Wann ist es angebracht, EMPTY_LIST im Gegensatz zu emptyList() zu verwenden?

+1

Für alle Java Generics Fragen, empfehle ich "[Java Generics und Sammlungen] (http://oreilly.com/catalog/9780596527754/)" von Maurice Naftalin, Philip Wadler. –

Antwort

389

Das Problem, das Sie stoßen sind, ist, dass, obwohl die Methode emptyList()List<T> zurückkehrt, haben Sie es nicht mit der Art versehen, so wird standardmäßig List<Object> zurück.Sie können den Typ Parameter liefern und Ihren Code verhalten haben, wie erwartet, wie folgt aus:

public Person(String name) { 
    this(name,Collections.<String>emptyList()); 
} 

Nun, wenn Sie gerade Zuordnung tun, kann der Compiler herauszufinden, die generische Typparameter für Sie aus. Es heißt Typinferenz. Zum Beispiel, wenn Sie dies tun:

public Person(String name) { 
    List<String> emptyList = Collections.emptyList(); 
    this(name, emptyList); 
} 

dann der emptyList() Anruf zurückkehren würde richtig ein List<String>.

+10

Verstanden. Aus der ML-Welt kommend ist es für mich seltsam, dass Java den richtigen Typ nicht ableiten kann: Der Typ des formalen Parameters und der Rückgabetyp von emptyList sind eindeutig vereinheitlichbar. Aber ich denke, der Typhintermittler kann nur "kleine Schritte" machen. –

+5

In einigen einfachen Fällen scheint es möglich, dass der Compiler in diesem Fall auf den fehlenden Typparameter zurückführt - aber das könnte gefährlich sein. Wenn mehrere Versionen der Methode mit unterschiedlichen Parametern existieren, können Sie am Ende die falsche aufrufen. Und die zweite vielleicht noch nicht einmal existiert ... –

+7

Diese Notation "Collections. emptyList()" ist wirklich seltsam, macht aber Sinn. Einfacher als Enum >. :) –

85

Sie verwenden möchten:

Collections.<String>emptyList(); 

Wenn Sie an der Quelle suchen, was emptyList tut man sehen, dass es eigentlich nur ein

return (List<T>)EMPTY_LIST; 
26

funktioniert die emptyList Methode, um diese Signatur hat:

public static final <T> List<T> emptyList() 

Das <T> vor dem Wort Liste bedeutet, dass es den Wert der g ergibt generischer Parameter T aus dem Typ der Variablen, der das Ergebnis zugewiesen ist. Also in diesem Fall:

List<String> stringList = Collections.emptyList(); 

Der Rückgabewert wird dann durch eine Variable vom Typ explizit verwiesen List<String>, so kann der Compiler es herausfinden. In diesem Fall:

setList(Collections.emptyList()); 

Es gibt keine expliziten Rückgabevariable für den Compiler den generischen Typ, um herauszufinden, zu verwenden, so wird standardmäßig Object.