2014-12-31 9 views
5

Die folgenden Code kompiliert (und führt Tests wie erwartet) in Eclipse:Discrepancy zwischen Eclipse-Compiler und Javac - Aufzählungen, Schnittstellen und generics

import java.util.EnumSet; 
public class EnumTest { 

    static enum Cloneables implements Cloneable { 
     One, Two, Three; 
    } 

    public <T extends Cloneable> T getOne(Class enumType) { 
     EnumSet<? extends T> set = EnumSet.allOf(enumType); 
     return set.iterator().next(); 
    } 
} 

jedoch mit dem Kompilieren entweder javac (JDK 7) direkt oder über Maven schlägt mit dem folgenden Fehler:

type argument ? extends T is not within bounds of type-variable E 

Um ehrlich zu sein, die Komplexität von Aufzählungen + Schnittstellen + Typ-Parameter (Generika) alle im Spiel auf einmal warf mich ab, als ich den Code schrieb, aber ich dachte, ich hatte es endlich richtig gemacht.

Ziel Aufruf Code wie diesen zu schreiben ist:

Cloneable something = enumTest.getOne(Cloneables.class); 

Zum Beispiel in Eclipse den folgenden Test erstellt und übergibt:

@Test 
public void testGetFirst() { 
    assertSame(Cloneables.One, getOne(Cloneables.class)); 
} 

irgendwelche Hinweise über die "korrekt" Eclipse- oder javac, werden geschätzt.

auch eine Beratung über alternative Möglichkeiten geschätzt ist, die Idee umzusetzen: nehmen Sie eine Klasse als Methode param, die in EnumSet.allOf() verwendet werden können, und dass bestimmt auch die Art der Enum-Objekte in den EnumSet

By the way , bemühe dich nicht, den Zweck dieser Methode zu kritisieren; Ich habe es von nützlicheren/sinnvolleren Code heruntergesetzt. Ich bin nicht daran interessiert, die Vorzüge des "Findens des ersten Elements von einem Enum-Typ" zu diskutieren - das ist nicht der Sinn dieser Frage.

+0

Es scheint, dass dies in javac (JDK 1.7.0_60) ein Fehler sein kann. Die akzeptierte Antwort unten ist ein Workaround (und tatsächlich sauberer Code). Siehe die detaillierte Analyse unter https://bugs.eclipse.org/bugs/show_bug.cgi?id=456459#c7 –

Antwort

4

Sie müssen sicherstellen, dass T ist ein Aufzählungstyp, oder es werden die Einschränkungen für EnumSet nicht erfüllen:

public <T extends Enum<T> & Cloneable> T getOne(Class enumType) 

Auch Sie den Platzhalter nicht brauchen in Ihrem EnumSet, und Sie soll nicht den rohen Class-Typ:

public <T extends Enum<T> & Cloneable> T getOne(Class<T> enumType) { 
    EnumSet<T> set = EnumSet.allOf(enumType); 
    return set.iterator().next(); 
} 
+1

Ich wusste nicht einmal, dass Sie den Operator '&' anwenden können, wenn Sie einen Typparameter wie diesen deklarieren. Selbst nach fast 18 Jahren Java-Code habe ich heute etwas Neues gelernt! –

+0

Übrigens, der Grund, warum ich die rohe Form von 'Class' verwendet habe, ist, dass ohne 'T' extending' Enum' der Eclipse-Compiler keinen Wert für den 'Class' Typparameter akzeptiert. Die Verwendung von Raw war ein Nebeneffekt des Nichtwissens, 'Cloneable' und' Enum' für 'T' zu kombinieren. –

+0

Interessanterweise wird Eclipse nicht zulassen, dass ich den Satz als 'EnumSet ' deklariere - es klagt, dass * "Bound Mismatch: Der Typ T ist kein gültiger Ersatz für den begrenzten Parameter > vom Typ EnumSet " *. Zum Glück, EnumSet