将Java泛型与枚举一起使用
更新:感谢所有帮助过我的人——这个问题的答案在于我在更复杂的代码中没有注意到什么,以及我对Java5协变返回类型不知道什么 原创帖子: 今天早上我一直在胡闹。虽然我知道我可以用不同的方式解决整个问题,但我发现自己一直在想为什么它没有按照我预期的方式工作。在花了一些时间阅读这篇文章之后,我发现我还没有接近理解,所以我把它作为一个问题提出来,看看我是否只是在做傻事,或者是否真的有什么我不理解的事情发生在这里 我创建了一个自定义事件层次结构,如下所示:将Java泛型与枚举一起使用,java,generics,enums,switch-statement,Java,Generics,Enums,Switch Statement,更新:感谢所有帮助过我的人——这个问题的答案在于我在更复杂的代码中没有注意到什么,以及我对Java5协变返回类型不知道什么 原创帖子: 今天早上我一直在胡闹。虽然我知道我可以用不同的方式解决整个问题,但我发现自己一直在想为什么它没有按照我预期的方式工作。在花了一些时间阅读这篇文章之后,我发现我还没有接近理解,所以我把它作为一个问题提出来,看看我是否只是在做傻事,或者是否真的有什么我不理解的事情发生在这里 我创建了一个自定义事件层次结构,如下所示: public abstract class Ab
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;
}
}
其中,我的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()会导致编译器告诉我无法打开枚举,只能打开可转换的int值或枚举常量
可以将以下方法添加到MyEvent:
public Type getId()
{
return super.getId();
}
一旦我这么做了,一切都会按照我的预期进行。我不仅对找到一个解决方法感兴趣(因为我显然有一个),我还对人们可能有什么见解感兴趣,为什么这不能像我一开始就预期的那样起作用。这与泛型无关。java中enum的Switch语句只能使用该特定enum的值,因此禁止实际指定enum名称。这应该起作用:
switch(event.getId()) {
case SELECTED:
l.selected(event);
break;
case SELECTION_CLEARED:
l.unselect(event);
break;
}
更新:好的,这是我复制/粘贴、编译和运行的一段实际代码(我必须对其进行一点更改,以使其在编译时没有依赖项)-没有错误:
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;
}
}
这可以在Java1.5下编译和运行。我在这里遗漏了什么?简而言之,因为T被擦除为一个Enum类,而不是一个Enum常量,所以编译后的语句看起来像是打开了getID为Enum的结果,如以下签名所示:
Enum getId();
当您使用特定类型覆盖它时,您正在更改返回值并可以打开它
编辑:阻力让我好奇,所以我突然想出了一些代码:
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());
}
public enum Num{
一,,
两个
}
公共抽象类抽象{
公共摘要T getId();
}
公共抽象类Real扩展了抽象{
}
公共静态void main(字符串[]args)引发异常{
方法m=Real.class.getMethod(“getId”);
System.out.println(m.getReturnType().getName());
}
结果是java.lang.Enum,而不是Num.T在编译时被擦除为Enum,因此无法打开它
编辑:
我测试了每个人都在使用的代码示例,它们也适用于我,因此尽管我怀疑这是更复杂的实际代码中的问题的根源,但发布的示例实际上编译并运行良好。Yishai是正确的,神奇的短语是“”,这是Java 5.0中新增的--您不能打开Enum,但您可以打开扩展枚举的类型类。MyEvent继承的AbstractEvent中的方法会被类型擦除。通过重写它,您将
getId()
的结果重定向到您的类型类,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;
}
}
对我有用
完整的剪切和粘贴测试程序:
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;
}
}
}
enum MyEnum{
A、 B
}
类摘要{
私人期末考试;
公开摘要(E){
这个。e=e;
}
公共E get(){
返回e;
}
}
类派生的抽象扩展{
公共派生(){
超级(髓鞘A);
}
公共静态int-sw(派生){
开关(派生的.get()){
案例A:返回1;
默认值:返回342;
}
}
}
您使用的是某种特殊的编译器吗?只有当我们对
AbstractEvent
有一个原始引用时,这才有意义。这就是我所想的——自动装箱由于某种原因不起作用——但为什么是重要的问题。不,fireEvent只适用于具体的MyEvent类。没有人处理AbstractEvent-这对我来说只是一种方便。@Yishai,类型确实在运行时被删除,并且getName()
将返回Enum,但是在编译时,编译器可以访问参数化类型。@Yishai-啊,但是您已经测试了整个不同的问题。Chris的代码显式地将MyEvent.Type实例传递给他的MyEvent构造函数,所以他的开关工作正常。不,恐怕不行。如果是这样的话,那么它将抱怨我的case:line,而不是switch语句中的event.getId()。此外,如果我在我的具体类中重写getId(),只返回super的getId(),那么一切都会正常工作。关于大小写标签的限定,您当然是正确的,但是真正让我困惑的问题是,为什么event.getId()不是一个有效的可切换参数。对于您的代码,它最适合我-没有覆盖。现在,如果firevent()
是以AbstractEvent
作为参数类型声明的,那么情况就不同了。不,firevent()使用的是具体的类。我不知道。。。我使用的是JDK1.6,但我不认为会有什么不同……这太棒了——我错过了Java5的这个特性,完全是因为指针!回顾并梳理代码之后,我没有注意到我在抽象类中定义了第二个getId()方法(显然它比我在这里发布的示例更复杂)。正是这第二个getId()返回的枚举被switch语句调用,把我搞砸了!链接似乎已失效。我正在通过EclipseChris使用Java 6编译器(r14),您能显示包含fireEvent
的类的类声明吗?问题是我的AbstractEvent太多了
Enum getId();
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());
}
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;
}
}
}
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;
}
}
}