我有这样的结构:
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);
),问题是:这个新版本安全吗?
答案 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
找到答案