2009-07-23 2 views
17

Обновление: Спасибо за каждого, кто помог - ответ на этот вопрос лежал в том, что я не замечал в своем более сложном коде и что я не знал о ковариантных возвращаемых типах Java5 ,Использование Java Generics with Enums

Оригинал сообщения:

Я играл вокруг с чем-то этим утром. Хотя я знаю, что я мог решить эту проблему по-другому, я нахожу себя одержимым, выясняя, почему он не работает так, как я ожидал. Проведя некоторое время, читая об этом, я нахожу, что я не ближе к пониманию, поэтому я предлагаю его в качестве вопроса, чтобы увидеть, насколько я просто глуп, или если я действительно не понимаю, что происходит здесь ,

Я создал пользовательские иерархии событий, как так:

public abstract class AbstractEvent<S, T extends Enum<T>> 
{ 
    private S src; 
    private T id; 

    public AbstractEvent(S src, T id) 
    { 
     this.src = src; 
     this.id = id; 
    } 

    public S getSource() 
    { 
     return src; 
    } 

    public T getId() 
    { 
     return id; 
    } 
} 

С конкретной реализации, как так:

public class MyEvent 
extends AbstractEvent<String, MyEvent.Type> 
{ 
    public enum Type { SELECTED, SELECTION_CLEARED }; 

    public MyEvent(String src, Type t) 
    { 
     super(src, t); 
    } 
} 

А потом создать событие следующим образом:

fireEvent(new MyEvent("MyClass.myMethod", MyEvent.Type.SELECTED)); 

Где мой fireEvent определяется как:

Поэтому я думал, что это будет довольно просто, но оказывается, что вызов event.getId() приводит к тому, что компилятор говорит мне, что я не могу включить Enums, только конвертируемые значения int или константы перечисления.

Можно добавить следующий метод MyEvent:

public Type getId() 
{ 
    return super.getId(); 
} 

После того, как я это делаю, все работает именно так, как я ожидал. Я не просто заинтересован в том, чтобы найти обходной путь для этого (потому что у меня, очевидно, есть), я интересуюсь любым прозрением, которое люди могут иметь, чтобы ПОЧЕМУ это не работает, поскольку я ожидал, что это спустится с места в карьер.

+0

Крис, можете ли вы показать объявление класса класса, содержащего «fireEvent»? – notnoop

+0

Проблема была в том, что мой AbstractEvent намного сложнее, чем то, что я разместил здесь. Я * должен * создать игрушку и проверить ее ** перед ** публикацией! Спасибо всем, кто помог! Вместо ответа было то, что мой метод getId() был «ковариантным типом возврата». У меня была другая реализация, похожая на класс, который был определен как возвращаемый тип Enum . Когда я отбросил это, оператор switch начал компилировать. –

+0

-0 по крайней мере подходит к чистоте ... –

ответ

10

Yishai is right, а волшебная фраза - «covariant return types», которая является новой версией Java 5.0 - вы не можете включить Enum, но вы можете включить класс Type, который расширяет Enum. Методы в AbstractEvent, которые наследуются MyEvent, подвержены стиранию типа. Переопределяя это, вы перенаправляете результат getId() на свой класс Type таким образом, который Java может обрабатывать во время выполнения.

+0

Это замечательно - я пропустил эту функцию Java5 полностью благодаря указателю! –

+0

Вернувшись и прочесывая код, я не заметил, что в своем абстрактном классе я определил второй метод getId() (очевидно, он был более сложным, чем пример, который я разместил здесь). Это был второй getId(), который возвратил Enum , вызываемый оператором switch и помешавший мне! –

+0

Ссылка кажется мертвой. – FDinoff

8

Это не относится к дженерикам. Оператор switch для enum в java может использовать только значения этого перечисления, поэтому запрещен, чтобы указать имя перечисления. Это должно работать:

switch(event.getId()) { 
    case SELECTED: 
     l.selected(event); 
     break; 
    case SELECTION_CLEARED: 
     l.unselect(event); 
     break; 
} 

Update: Хорошо, вот реальный код (который я должен был изменить немного, чтобы получить его скомпилировать без зависимостей), которые я копировать/вставить, составленный и побежал нет - нет ошибок:

AbstractEvent.java

public abstract class AbstractEvent<S, T extends Enum<T>> { 
    private S src; 
    private T id; 

    public AbstractEvent(S src, T id) { 
     this.src = src; 
     this.id = id; 
    } 

    public S getSource() { 
     return src; 
    } 

    public T getId() { 
     return id; 
    } 
} 

MyEvent.java

public class MyEvent extends AbstractEvent<String, MyEvent.Type> { 
    public enum Type { SELECTED, SELECTION_CLEARED }; 

    public MyEvent(String src, Type t) { 
     super(src, t); 
    } 
} 

Test.java

public class Test { 
    public static void main(String[] args) { 
     fireEvent(new MyEvent("MyClass.myMethod", MyEvent.Type.SELECTED)); 
    } 

    private static void fireEvent(MyEvent event) { 
     switch(event.getId()) { 
      case SELECTED: 
       System.out.println("SELECTED"); 
       break; 
      case SELECTION_CLEARED: 
       System.out.println("UNSELECTED"); 
       break; 
     } 
    } 
} 

компилируется и работает под управлением Java 1.5 просто отлично. Что мне здесь не хватает?

+0

Нет, я боюсь, что это не сработает. Если бы это было так, тогда было бы жаловаться на мой случай: строки, а не мой event.getId() в инструкции switch. Кроме того, если я переопределяю getId() в моем конкретном классе и возвращу только getId(), все это работает. –

+0

Прошу прощения: я ввел неверный пример. Вы, конечно, правильно относитесь к квалификации ярлыков case, однако реальная проблема, которая меня озадачивает, - почему event.getId() не является допустимым параметром, доступным для коммутатора. –

+0

Это, безусловно, работает для меня с вашим кодом - без переопределений. Теперь, если 'fireEvent()' был объявлен с помощью 'AbstractEvent' в качестве типа параметра, это было бы другой историей. – ChssPly76

2

В очень короткий, потому что T стерт с классом Enum, не Enum постоянной, поэтому скомпилированный заявление выглядит, как вы переключаетесь на результат GetId быть Enum, так как в этой подписи:

Enum getId(); 

Когда вы переопределяете его определенным типом, вы меняете возвращаемое значение и можете его включить.

EDIT: Сопротивление меня любопытный, поэтому я взбитый код:

public enum Num { 
    ONE, 
    TWO 
} 
public abstract class Abstract<T extends Enum<T>> { 
    public abstract T getId(); 
} 

public abstract class Real extends Abstract<Num> { 

} 

public static void main(String[] args) throws Exception { 
    Method m = Real.class.getMethod("getId"); 
    System.out.println(m.getReturnType().getName()); 
} 

Результата java.lang.Enum, не Num. T во время компиляции стирается до Enum, поэтому вы не можете его включить.

EDIT:

Я тестировал образцы кода все работают с, и они работают для меня, как хорошо, так что, хотя я подозреваю, что это лежит в основе этого вопроса в более сложном реальном коде, образец, как отвечал на самом деле компилируется и работает нормально.

+0

Это было бы важно, если бы у нас была исходная ссылка на 'AbstractEvent'. – notnoop

+0

Это то, о чем я думал, - что авто-бокс по какой-то причине не работает, но ПОЧЕМУ - важный вопрос. –

+0

Нет, fireEvent работает только с конкретным классом MyEvent. Никто не имеет отношения к AbstractEvent - это просто удобство для меня. –

1

Я просто попробовал это (скопировал ваш код), и я не могу дублировать ошибку компилятора.

public abstract class AbstractEvent<S, T extends Enum<T>> 
{ 
    private S src; 
    private T id; 

    public AbstractEvent(S src, T id) 
    { 
     this.src = src; 
     this.id = id; 
    } 

    public S getSource() 
    { 
     return src; 
    } 

    public T getId() 
    { 
     return id; 
    } 
} 

и

public class MyEvent extends AbstractEvent<String, MyEvent.Type> 
{ 

    public enum Type 
    { 
     SELECTED, SELECTION_CLEARED 
    }; 

    public MyEvent(String src, Type t) 
    { 
     super(src, t); 
    } 


} 

и

public class TestMain 
{ 
    protected void fireEvent(MyEvent event) 
    { 
     switch (event.getId()) 
     { 
      case SELECTED: 
      break; 
      case SELECTION_CLEARED: 
      break; 
     } 
    } 
} 
2

работает для меня.

Полное отключение и вклейки тестовая программа:

enum MyEnum { 
    A, B 
} 
class Abstract<E extends Enum<E>> { 
    private final E e; 
    public Abstract(E e) { 
     this.e = e; 
    } 
    public E get() { 
     return e; 
    } 
} 
class Derived extends Abstract<MyEnum> { 
    public Derived() { 
     super(MyEnum.A); 
    } 
    public static int sw(Derived derived) { 
     switch (derived.get()) { 
      case A: return 1; 
      default: return 342; 
     } 
    } 
} 

Вы используете какой-то особенный компилятор?

+0

Я использую компилятор Java 6 (r14) через Eclipse –