不兼容的java泛型类

时间:2016-04-26 15:17:44

标签: java generics types

似乎我再次陷入了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>(第一种情况) )但在我看来,我似乎试图掩盖我的误解,并且感觉不对。

2 个答案:

答案 0 :(得分:4)

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();
}

答案 1 :(得分: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. 您可以完全删除泛型,因为其中没有太多价值