繁体   English   中英

使用Class在Java中无法进行强制转换 <T> 并且&lt;T扩展A&gt;

[英]Cast doesn't work in java using Class<T> and < T extends A>

我对Java编译器有一个奇怪的问题。 这是代码:

private <T extends IdentifiedBusinessTransversalEntity> T 
    getOrCreateTransversalEntity(Class<T> classT, String id) {
...}

private <T extends IdentifiedBusinessDSEntity> T
    getOrCreateDSEntity(Class<T> classT, String id) {
...}

public abstract  class IdentifiedBusinessDSEntity extends
        BusinessDSEntity implements IdentifiedEntity {
...}

public abstract class IdentifiedBusinessTransversalEntity extends
                BusinessTransversalEntity implements IdentifiedEntity {
 ...}

public <T extends IdentifiedEntity> T getOrCreate(Class<T> classT, String id)
{
    if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT))
    {
        return getOrCreateDSEntity(classT.asSubclass(IdentifiedBusinessDSEntity.class),id);
    } else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT))
    {   //must cast explicitly to T here but works well just above and is exactly the same. Strange
        return (T) getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class),id);
    }
    return null;
}

我不明白为什么在getOrCreate函数中,编译器允许第一次返回不带(T)但不允许第二次返回。 错误是:

类型不匹配:无法从IdentifiedBusinessTransversalEntity转换为T

没关系: getOrCreateTRansversalEntity返回实现IdentifiedEntityIdentifiedBusinessTransversalEntity的子类。 最奇怪的是,第一次返回就可以,这是完全对称的。 有什么区别?

该代码不应编译。

我相信您是犯了一个错误,使您相信它可以运行,或者您的编译器中有错误。 在Eclipse 4.5.1和Javac 1.8.0_45中, getOrCreate两个返回语句都为我提供了编译错误。

说明

编辑: 我改变了这种解释,我想我误解了这个问题。

在说明中,我将getAndCreate的类型参数的名称更改为C以避免与其他类型参数混淆。

问题是在asSubclass方法中,该类是C的子类的信息丢失了; 剩下的唯一信息是该类是例如IdentifiedBusinessDSEntity的子类。

asSubclass具有以下类型:

<U> Class<? extends U> asSubclass(Class<U> clazz)

如我们所见,返回类型中不存在接收器类T的原始类型参数。

声明getOrCreate返回C 这就是您需要强制转换的原因:将类型C重新引入返回值。

替代asSubclass

我们可以想象asSubclass具有以下类型:

<U> Class<? extends U & T> asSubclass(Class<U> clazz)

那将是类型安全的,并且使用该返回类型,您的代码将无需强制转换即可编译。 但是在Java中不允许使用多个类型参数作为边界。

以下是我用来调查问题的代码:

class Test {
    interface IdentifiedEntity {}
    class BusinessDSEntity {}
    class BusinessTransversalEntity {}

    private <T extends IdentifiedBusinessTransversalEntity> T getOrCreateTransversalEntity(Class<T> classT, String id) {
        return null;
    }

    private <T extends IdentifiedBusinessDSEntity> T getOrCreateDSEntity(Class<T> classT, String id) {
        return null;
    }

    public abstract  class IdentifiedBusinessDSEntity 
        extends BusinessDSEntity implements IdentifiedEntity {}

    public abstract class IdentifiedBusinessTransversalEntity 
        extends BusinessTransversalEntity implements IdentifiedEntity {}

    public <C extends IdentifiedEntity> C getOrCreate(Class<C> classT, String id) {
        if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT)) {
            // Error here. Note that the type of s does not contain C.
            Class<? extends IdentifiedBusinessDSEntity> s = classT.asSubclass(IdentifiedBusinessDSEntity.class);
            return getOrCreateDSEntity(s, id);
        } else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT)) {
            // Also error here
            return getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class), id);
        }
        return null;
    } 
}

可能的解决方案

我想不出任何保留很多类型安全性的真正优雅的解决方案。

一种替代方法是将返回类型的类传递给实体特定的方法。 这非常丑陋且不便,您必须手动验证该类确实是正确的类型。

即使丢失某些类型安全性,也最好仅强制转换返回值。

例:

class Test {
    interface IdentifiedEntity {}
    class BusinessDSEntity {}
    class BusinessTransversalEntity {}

    private <R extends IdentifiedEntity, T extends IdentifiedBusinessDSEntity> 
        R getOrCreateDSEntity(Class<T> classT, Class<R> classR, String id)
    {
        // Verify that classT really is subclass of classR.
        classT.asSubclass(classR);
        return null;
    }

    private <R extends IdentifiedEntity, T extends IdentifiedBusinessTransversalEntity> 
        R getOrCreateTransversalEntity(Class<T> classT, Class<R> classR, String id)
    {
        // Verify that classT really is subclass of classR.
        classT.asSubclass(classR);
        return null;
    }

    public abstract  class IdentifiedBusinessDSEntity 
        extends BusinessDSEntity implements IdentifiedEntity {}

    public abstract class IdentifiedBusinessTransversalEntity 
        extends BusinessTransversalEntity implements IdentifiedEntity {}

    public <C extends IdentifiedEntity> C getOrCreate(Class<C> classT, String id) {
        if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT)) {
            return getOrCreateDSEntity(classT.asSubclass(IdentifiedBusinessDSEntity.class), classT, id);
        } else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT)) {
            return getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class), classT, id);
        }
        return null;
    }
}

我会去的。 这种解释是基于观察而不是基于显性知识,因此,如果我得到纠正,请不要感到惊讶。 我认为您感到困惑的是<T extends IdentifiedBusinessDSEntity> T返回类型声明的含义。 在我看来,该返回声明正在创建一个全新的类型 ,而不是描述已经声明的类型。 换句话说,您可以拥有Class1,因为Class1 extends ClassA ,但是当您返回T extends ClassA不是在描述Class1,而是一个全新的类(或类型)。 您不能期望这个新类与Class1相同,而是与您要返回ClassX extends ClassA ClassX相同:Class1和ClassX是不同的类。

似乎您正在尝试通过缓存实现Factory Design模式。 该代码可以做到这一点,并且应该与您在上面所做的非常相似。 假设工厂应该生成Interface实现,则无需将返回类型声明为扩展接口的类,只需返回接口即可。

public class Factory {

    interface I {}

    List<I> iCache = new ArrayList<I>();

    abstract class ClassA {}
    abstract class ClassB {}

    class Class1 extends ClassA implements I {}
    class Class2 extends ClassB implements I {}

    I getOrCreateTypeA() { 
        for( I cls: iCache ) {
            if( cls instanceof ClassA ) return cls;
        }
        Class1 cls = new Class1();
        iCache.add(cls);
        return cls;
    }
    I getOrCreateTypeB() { 
        for( I cls: iCache ) {
            if( cls instanceof ClassB ) return cls;
        }
        Class2 cls = new Class2();
        iCache.add(cls);
        return cls;
    }

    I getOrCreate(Class<?> cls) {
        if ( ClassA.class.isAssignableFrom(cls)) {
            return getOrCreateTypeA();
        } else if ( ClassB.class.isAssignableFrom(cls)) {
            return getOrCreateTypeB();
        }
        return null;
    }

    void run() {
        I classI1 = getOrCreate(Class1.class);
        System.out.println(classI1);
        I classI2 = getOrCreate(Class2.class);
        System.out.println(classI2);
        I classI3 = getOrCreate(Class1.class);
        System.out.println(classI3);
        System.out.println(iCache);
    }

    public static void main(String... args) {
        new Factory().run();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM