Вопрос по java, generics, switch-statement, enums – Использование Java Generics с Enums
Update: Спасибо всем, кто помог - ответ на этот вопрос заключался в том, что я не заметил в моем более сложном коде и что я не знал о ковариантных типах возвращаемых данных Java5.
Original Post:
Я этим утром что-то играл. Хотя я знаю, что яcould Решить эту проблему по-другому, я нахожу себя одержимым выяснением, почему это не работает так, как я ожидал. Потратив некоторое время на чтение этого, я не нахожу себя ближе к пониманию, поэтому я предлагаю это в качестве вопроса, чтобы посмотреть, не глупа ли я, или я действительно что-то не понимаю, что происходит здесь ,
Я создал пользовательскую иерархию событий примерно так:
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 определяется как:
protected void fireEvent(MyEvent event)
{
for(EventListener l : getListeners())
{
switch(event.getId())
{
case SELECTED:
l.selected(event);
break;
case SELECTION_CLEARED:
l.unselect(event);
break;
}
}
}
Поэтому я подумал, что это будет довольно просто, но оказывается, что вызов event.getId () приводит к тому, что компилятор говорит мне, что я не могу включить Enums, только конвертируемые значения int или константы enum.
Можно добавить следующий метод в MyEvent:
public Type getId()
{
return super.getId();
}
Как только я это делаю, все работает именно так, как я ожидал. Я не просто заинтересован в поиске обходного пути для этого (потому что у меня, очевидно, есть такой способ), я заинтересован в том, чтобы у людей было понимание того, ПОЧЕМУ это не работает, поскольку я ожидал, что это сразу.
fireEvent
?
Ишай прав, и волшебная фраза & quot;ковариантные типы возврата& Quot; который является новым в Java 5.0 - вы не можете включить Enum, но вы можете включить свой класс Type, который расширяет Enum. Методы в AbstractEvent, которые наследуются MyEvent, подлежат стиранию типа. Переопределяя его, вы перенаправляете результатgetId()
к вашему классу Type способом, который 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;
}
}
а также
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;
}
}
}
Это не связано с дженериками. Оператор switch для enum в java может использовать только значения этого конкретного enum, таким образом, онprohibited на самом деле указать имя перечисления. Это должно работать:
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 просто отлично. Что мне здесь не хватает?
fireEvent()
был объявлен сAbstractEvent
как тип параметра, это была бы другая история.
Короче говоря, поскольку T стирается в класс Enum, а не в константу Enum, поэтому скомпилированный оператор выглядит так, как будто вы включаете в качестве результата getID Enum, как в этой сигнатуре:
Enum getId();
Когда вы переопределяете его определенным типом, вы меняете возвращаемое значение и можете включить его.
EDIT: The resistance made me curious, so I whipped up some code:
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());
}
The result is java.lang.Enum, not Num. T is erased to Enum at compile time, so you can't switch on it.
РЕДАКТИРОВАТЬ:
Я протестировал примеры кода, с которыми работают все, и они работают и на меня, поэтому, хотя я подозреваю, что это лежит в основе проблемы более сложного реального кода, пример, который был опубликован, на самом деле компилируется и работает нормально.
getName()
вернет Enum, но во время компиляции у компилятора есть доступ к параметризованным типам.
AbstractEvent
.
Works for me.
Полная тестовая программа вырезания и вставки:
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;
}
}
}
Вы используете какой-то особенный компилятор?