将泛型用作 return 类型

usage of generics as return type

我有这样的结构:

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);),问题是:这个新版本安全吗?

能给您信心的约束条件是限制您的工厂收到的 classes:

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 的子class 时,编译器将不接受任何创建调用。但是,它会接受一个摘要 class。如果你想知道你收到了具体的class,你可以在这里找到答案How can I determine whether a Java class is abstract by reflection

强制转换不安全的原因是这一行:

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

这从 调用站点 推断出 return 类型;换句话说,考虑以下 class:

public abstract class MyFakeDomain extends MyDomain { }

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

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

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

类型推断也是强制转换必须存在的原因;虽然 FirstBarDomain 绝对是 MyDomain 的子 class,但我们不知道它是否属于 T 类型,因为 T 可能是 any MyDomain subclass, 不一定FirstBarDomain.

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

这给了我们第二个问题的答案:使用 BarDomain 作为要推断的类型并不总是安全的,因为它可能是 MyDomain 的另一个子 class .唯一在这里始终安全的类型是 MyDomain - 但是,如果您计划只使用 MyDomain 作为类型,您不妨删除泛型类型绑定并只使 return 输入 MyDomain.