Java泛型的不兼容类

8

看来我又遇到了Java泛型问题。以下是我的情况:

有几个类:

class CoolIndex implements EntityIndex<CoolEntity>

class CoolEntity extends BaseEntity

使用上述类的枚举:

enum Entities {
    COOL_ENTITY {
        @Override
        public <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls() {
            return CoolIndex.class;
        }

        @Override
        public <E extends BaseEntity> Class<E> getEntityCls() {
            return CoolEntity.class;
        }
    }

    public abstract <E extends BaseEntity, I extends EntityIndex<E>> Class<I> getIndexCls();

    public abstract <E extends BaseEntity> Class<E> getEntityCls();    
}

我需要通过调用getIndexCls()函数返回的结果来调用另一个函数:

static <E extends BaseEntity, I extends EntityIndex<E>> boolean isSomeIndexViewable(Class<I> cls)

问题在于编译器抱怨return CoolIndex.class;return CoolEntity.class;,但我不清楚为什么会这样……当然,我可以将其转换为Class<I>(第一种情况),但我感觉好像是在掩饰自己的困惑,这并不正确。

相关(可能会有帮助):https://dev59.com/V3vZa4cB1Zd3GeqP9wOe - sp00m
另外还有几个策略:https://dev59.com/Vmgu5IYBdhLWcg3wOUgk - azurefrog
一个非常类似的问题,与http://stackoverflow.com/questions/28234960/java-generics-and-enum-loss-of-template-parameters相似。 - EpicPandaForce
2个回答

3

getIndexCls的问题在于,由于它是通用的,类型参数可以被解释为符合声明边界的任何类。您可能认为CoolIndex.class符合这些边界,实际上它确实符合,但调用者可以提供自己的类型参数,这些参数可能不兼容,例如:

Entities.COOL_ENTITY.<UncoolEntity, UncoolIndex>getIndexCls();

那样会破坏类型安全性,因此编译器不允许这样做。您可以将其转换为Class<I>,但由于同样的原因,编译器会警告您进行未经检查的转换。它会编译,但可能会导致运行时问题,如我所述。
其他情况可以通过传递一个Class<I>对象来使类型推断正常工作,但这违反了这个方法的目的 -- 返回一个Class<I>对象。
其他情况需要将泛型类型参数从方法移动到类中,但您正在使用枚举,它们无法是泛型的。
我想到的唯一类似编译的方法是完全删除enum。使用抽象类,以便您可以声明类级别的类型参数。使用所需的类型参数实例化常量。
abstract class Entities<E extends BaseEntity, I extends EntityIndex<E>> {
    public static final Entities<CoolEntity, CoolIndex> COOL_ENTITY = new Entities<CoolEntity, CoolIndex>() {
        @Override
        public Class<CoolIndex> getIndexCls() {
            return  CoolIndex.class;
        }

        @Override
        public Class<CoolEntity> getEntityCls() {
            return CoolEntity.class;
        }
    };

    // Don't instantiate outside this class!
    private Entities() {}

    public abstract Class<I> getIndexCls();
    public abstract Class<E> getEntityCls();
}

感谢您提供的解决方案。无论如何,它看起来有点不太优美,但我想在这种情况下这是最“正确”的解决方案。 - mr.nothing

1
这可以通过更简单的示例进行复制:
public <E extends BaseEntity> E get() {
    return new BaseEntity(); // compilation error here
}

在这种声明中<E extends BaseEntity>的问题在于您的方法声称返回调用者应该请求的任何类型E的实例:
MyCoolEntity1 e = get(); // valid, E is MyCoolEntity1
MyCoolEntity2 e = get(); // valid, E is MyCoolEntity2

这段代码应该是编译时安全的,因此您需要将方法的结果转换为 E 类型。

public <E extends BaseEntity> E get() {
    return (E) new BaseEntity(); // no error, but unsafe warning
}

在你的示例中,它几乎相同,你声称返回类型为 Class<E> 的值:
public <E extends BaseEntity> Class<E> getEntityCls() 

但是需要返回一个具体的类 SomeEntity.class,这个类是 Class<CoolEntity>


好的,我应该怎么修复它?

  1. 你可以加入类型转换 return (Class<I>) CoolIndex.class; / return (Class<E>) CoolEntity.class;

  2. 你可以使用类替换枚举,因为枚举不能够有泛型,而类可以

  3. 你可以完全去除泛型,因为在这里没有太多价值


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接