Ковариантные возвращаемые типы в перечислениях Java
Как уже упоминалось в другом вопросе на этом сайте, что-то подобное не является законным:
public enum MyEnum {
FOO {
public Integer doSomething() { return (Integer) super.doSomething(); }
},
BAR {
public String doSomething() { return (String) super.doSomething(); }
};
public Object doSomething();
}
Это происходит из-за того, что ковариантные типы возвращаемых данных, очевидно, не работают с константами перечисления (снова разрушая иллюзию, что константы перечисления являются одноэлементными подклассами типа перечисления...) Итак, как насчет того, чтобы добавить немного обобщений: это допустимо?
public enum MyEnum2 {
FOO {
public Class<Integer> doSomething() { return Integer.class; }
},
BAR {
public Class<String> doSomething() { return String.class; }
};
public Class<?> doSomething();
}
Здесь все три возвращаются Class
объекты, но отдельные константы "более специфичны", чем тип enum в целом...
3 ответа
По сути, проблема в том, что тип времени компиляции MyEnum.FOO
является MyEnum
, а не конкретный сгенерированный подкласс. Вы можете увидеть это без какой-либо ковариации:
enum MyEnum {
FOO {
public void foo() {}
};
}
public class Test {
public static void main(String[] args) throws Exception
{
MyEnum.FOO.foo(); // Error
}
}
По сути, компилятор будет видеть только подписи, объявленные в MyEnum
,
Мы можем использовать возвращаемые типы Covariant в перечислениях Java, но мы не можем использовать этот дочерний тип для переменной, которой назначен возврат, в отличие от обычной иерархии классов.
public class EnumCovariance {
public static void main(String[] args) {
// Type mismatch: cannot convert from Class<capture#1-of ?> to Class<Integer>
Class<Integer> something = MyEnum2.FOO.doSomething();
Child child = new Child();
Base base = child;
// ok
Class<Integer> something3 = child.doSomething();
// Type mismatch: cannot convert from Class<capture#2-of ?> to Class<Integer>
Class<Integer> something2 = base.doSomething();
}
}
abstract class Base {
public abstract Class<?> doSomething();
}
class Child extends Base {
@Override
public Class<Integer> doSomething() {
return Integer.class;
}
}
enum MyEnum2 {
FOO {
public Class<Integer> doSomething() {
return Integer.class;
}
},
BAR {
public Class<String> doSomething() {
return String.class;
}
};
public abstract Class<?> doSomething();
}
Этот код хорошо работает:
public enum MyEnum {
FOO {
@Override
public Integer doSomething() {
return (Integer)super.doSomething();
}
},
BAR {
@Override
public String doSomething() {
return (String)super.doSomething();
}
};
public Object doSomething() {
System.err.println( this );
return null;
}
public static void main( String[] args ) {
MyEnum toto = MyEnum.FOO;
System.err.println( toto.doSomething() );
}
}
вывод:
FOO
null
этот код не компилируется, ошибка "The return type is incompatible with MyEnum2.doSomething()"
, Cannot cast from Class<Object> to Class<Integer>
а также Cannot cast from Class<Object> to Class<String>
:
public enum MyEnum2 {
FOO {
@Override
public Class<Integer> doSomething() {
return (Class<Integer>)super.doSomething();
}
},
BAR {
@Override
public Class<String> doSomething() {
return (Class<String>)super.doSomething();
}
};
public Class<Object> doSomething() {
System.err.println( this );
return null;
}
public static void main( String[] args ) {
MyEnum2 toto = MyEnum2.FOO;
System.err.println( toto.doSomething() );
}
}