使用泛型作为返回类型

时间:2015-06-04 09:21:40

标签: java generics return type-safety

我有这样的结构:

abstract class MyDomain{...}
abstract class FooDomain extends MyDomain{...}
abstract class BarDomain extends MyDomain{...}
class FirstConcreteBarDomain extends BarDomain{...}
class SecondConcreteBarDomain extends BarDomain{...}

我需要一个创建MyDomain个对象的工厂。我的第一次尝试是这样的:

public interface ISpecializedObjectsFactory {
    public <T extends MyDomain> T create(Class<?> clazz);
}

实施为:

public class FirstSpecializedObjectsFactory implements ISpecializedObjectsFactory {

    @Override
    public <T extends MyDomain> T create(Class<?> clazz) {
        if(clazz.equals(BarDomain.class))
            return new FirstBarDomain();
        throw new InvalidParameterException();
    }

SecondBarDomain相同。

第一个问题:为什么会产生一个错误,指出它无法将FirstBarDomain强制转换为T

在此错误之后,我引入了一个演员:return (T) new FirstBarDomain();

问题是演员表是不安全的,我想对结果充满信心,所以我引入了另一个约束(假设每个MyDomain对象总是有2个派生级别):

public <T extends AnagrafeDomain, S extends T> S create(Class<T> clazz)

第二个问题:假设这个工厂是创建MyDomain个对象的唯一入口点,并且对工厂的调用从不使用具体类(但总是如下: BarDomain subj = SpecializedObjectsFactory.getFactory().create(BarDomain.class);),问题是:这个新版本安全吗?

2 个答案:

答案 0 :(得分:2)

演员阵容不安全的原因在于这条特殊的界限:

public <T extends MyDomain> T create(Class<?> clazz) {

这会从呼叫网站中推断出返回类型;换句话说,请考虑以下类:

public abstract class MyFakeDomain extends MyDomain { }

然后将编译以下代码,但在运行时失败:

ISpecializedObjectsFactory factory = new FirstSpecializedObjectsFactory();
MyFakeDomain broken = factory.create(BarDomain.class);

由于类型推断,这将抛出ClassCastException;推断类型将为MyFakeDomain,导致尝试将FirstBarDomain投射到MyFakeDomain,这是非法投射 - 因此是不安全警告。

类型推断也是演员必须出现的原因;虽然FirstBarDomain绝对是MyDomain的子类,但我们不知道它是T类型,因为T可能是任何 {{ 1}}子类,不一定是MyDomain

但是,如果调用者小心,您的代码将正常工作 - 无论您认为这是否可接受都取决于您。

这为我们提供了第二个问题的答案:使用FirstBarDomain作为要推断的类型并不总是安全的,因为它可能是BarDomain的另一个子类。这里唯一安全的类型是MyDomain - 但是,如果您计划仅使用MyDomain作为类型,那么您也可以删除泛型类型绑定并只返回类型{ {1}}。

答案 1 :(得分:0)

让您信心十足的限制条件是限制工厂收到的类别:

public interface ISpecializedObjectsFactory {
    public <T extends MyDomain> T create(Class<? extends MyDomain> clazz);
}


public class FirstSpecializedObjectsFactory implements ISpecializedObjectsFactory {
    @Override
    public <T extends MyDomain> T create(Class<? extends MyDomain> clazz) {
        if(clazz.equals(BarDomain.class))
            return (T) new FirstBarDomain();
        throw new InvalidParameterException();
    }
}

当参数不是MyDomain的子类时,编译器不接受任何创建调用。但是,它会接受一个抽象类。如果您想知道自己收到了具体课程,可以在How can I determine whether a Java class is abstract by reflection

找到答案