【问题标题】:Java enum implements interface with Class<T> return typeJava 枚举实现具有 Class<T> 返回类型的接口
【发布时间】:2015-05-29 04:46:49
【问题描述】:

好吧,乖点儿。

这是一个枚举,它使用返回“原始类型”的方法实现接口,这会在接口中的getCaste() 方法上给我一个警告。

public enum Thing implements HasCaste {

    Type1 {
        @Override
        public Class getCaste() {
            return String.class;
        }
    };
}

interface HasCaste {
    public Class getCaste();
}

如果我将接口的方法更改为:

public <T> Class<T> getCaste();

它在Type1 的方法签名上更改为未经检查的警告。如果我然后将Type1.getCaste()的签名更改为:

public <T> Class<T> getCaste()

然后返回有一个不兼容的类型错误,因为Class&lt;String&gt; 无法转换为Class&lt;T&gt;。如果我将返回值更改为(Class&lt;T&gt;) String.class,我们会收到未经检查的强制转换警告。

有没有办法在没有警告的情况下做到这一点?

编辑:

很抱歉我之前没有添加这个,但这样做会很好:

  , Type2 {
        @Override
        public Class getCaste() {
            return Integer.class;
        }
    };

【问题讨论】:

  • 对于您的编辑,您不能使用枚举。
  • @Radiodef 我实际上正计划链接那个问题:p(我是那个问题的提问者)
  • 是的,不幸的是,从技术上讲,枚举实际上可以携带泛型类型,因此这只是目前语言规范的限制。 (@EpicPandaForce 嗨:))
  • 声明public &lt;T&gt; Class&lt;T&gt; getCaste() 类型的方法是在告诉编译器方法的调用者 可以选择任何T 并返回Class&lt;T&gt;。但是,这实际上并非如此,因此您会收到警告。

标签: java generics enums


【解决方案1】:

你可以不指定返回类的类型参数:

public enum Thing implements HasCaste {

    Type1 {
        @Override
        public Class<String> getCaste() {
            return String.class;
        }
    }, Type2 {
        @Override
        public Class<Integer> getCaste() {
            return Integer.class;
        }
    };
}

interface HasCaste {
    public Class<?> getCaste();
}

【讨论】:

  • 协变覆盖是不可见的,因为常量声明只是Thing
  • @Radiodef,如何测试覆盖是否不可见?
  • 尝试像Class&lt;String&gt; c = Thing.Type1.getCaste();这样的作业。 ideone.com/ij7EwN
  • @Radiodef 如果可以,会违反 Liskov 吗?
  • 否,因为它仍然作为超类型(返回 Class&lt;?&gt;)。这里的问题是枚举常量的声明是Thing,因此定义覆盖的匿名子类是不可见的。
【解决方案2】:

正如在question I asked a while ago that was linked by Radiodef 中确定的那样,你不能用enum 来做到这一点,只能通过一个实际上是一个类的模拟枚举,像这样。

public abstract class Thing<T> implements HasCaste<T> {
    public static final Thing<String> Type1 = new Thing<String>() {
        @Override
        public Class<String> getCaste() {
            return String.class;
        }
    };
    public static final Thing<Integer> Type2 = new Thing<Integer>() {
        @Override
        public Class<Integer> getCaste() {
            return Integer.class;
        }
    };

    private Thing() {
    }
}

如您所见,这不是enum。仅使用enum 是不可能的,因为枚举不能有类型参数。

p.s.:如果你觉得这有帮助,请查看Radiodef's answer 以获得更完整的解释,毕竟这是我从他那里学来的:)

【讨论】:

  • 另外两个答案显然完成了任务。是不是正确的词?也许“不应该”会更好?
  • 如果你尝试在其他两个解决方案中访问Thing.Type1,那么你将不会得到Class&lt;String&gt;的类和Class&lt;Integer的Type2,你会得到@987654330 @ 本质上是Class&lt;Object&gt;.
  • ....哦,但是如果您只是想删除警告,那么可以肯定,Class&lt;?&gt; 有效。
【解决方案3】:

假设您想定义几个不同的“类型化”枚举常量,那么可以这样做:

interface HasCaste {
    public Class<?> getCaste();
}

public enum Thing implements HasCaste<?> {

    Type1(String.class),
    Type2(Integer.class);

    public final Class<?> clazz;

    private Thing(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public Class<?> getCaste() {
        return clazz;
    }
}

然后将输入延迟到 getCaste 的未来使用。枚举的主要特征是一个封闭的值域;必须列出类中所有可能的值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-28
    • 2012-10-06
    • 2021-12-22
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多