2014-12-31 1 views
5

Следующий код компилирует (и запускает тесты, как и ожидалось) в Eclipse:Расхождение между компилятором Eclipse, и JAVAC - Перечисления, интерфейсы и дженерики

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(); 
    } 
} 

Однако компиляции либо javac (JDK 7) непосредственно или через Maven не может со следующей ошибкой:

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

Чтобы быть честным, сложность перечислений + интерфейсы + тип-параметры (генерики) все в игре сразу же бросил меня, как я пишу код, но я думал, наконец, понял.

Цель состоит в том, чтобы написать вызывающий код как это:

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

Например, в Eclipse, следующий тест собирает и передает:

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

Есть улики, о которых является «правильным,» Затмение или javac, оцениваются.

Также понятный, какие-либо советы о альтернативных путях реализации идеи: взять класс как метод пары, которые могут быть использованы в EnumSet.allOf() и что также определяют тип объектов Enum в EnumSet

Кстати , не стоит критиковать цель этого метода; Я уменьшил его из более полезного/содержательного кода. Мне не интересно обсуждать достоинства «найти первый элемент из типа enum» - это не вопрос этого вопроса.

+0

оказывается, что это может быть ошибка в JAVAC (JDK 1.7.0_60). Принятый ниже ответ - это обход (и на самом деле более чистый код). См. Подробный анализ на https://bugs.eclipse.org/bugs/show_bug.cgi?id=456459#c7 –

ответ

4

Вы должны убедиться, что T является тип перечисления, или он не будет соответствовать ограничениям для EnumSet:

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

Кроме того, вам не нужно подстановочные знаки в вашем EnumSet, и вы не следует использовать необработанный Class тип:

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

Я даже не знал, что вы можете применить оператор '&' при объявлении такого параметра типа. Даже спустя почти 18 лет, написав Java-код, я узнал что-то новое сегодня! –

+0

Кстати, причина, по которой я использовал необработанную форму 'Class', заключается в том, что без' T', расширяющего 'Enum', компилятор Eclipse не принимал бы значения для параметра типа' Class'. Таким образом, использование этого сырья было побочным эффектом неведения для объединения «Cloneable» и «Enum» для 'T'. –

+0

Интересно, что Eclipse не позволит мне объявить набор как 'EnumSet ' - он жалуется, что * "Связанное несоответствие: тип T не является допустимым заменителем ограниченного параметра > типа EnumSet " *. К счастью, 'EnumSet