2015-06-04 224 views
2

我有一个结构是这样的:使用泛型作为返回类型

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

Implementend为:

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

第一个问题:为什么这会产生一个错误,指出它不能投出FirstBarDomainT

此错误后,我已经介绍了演员: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

答案之所以投是不安全的,因为这一行的:

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可能是任何MyDomain子,不一定FirstBarDomain

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

这给了我们第二个问题的答案:使用BarDomain作为推断的类型并不总是安全的,因为它可能是另一个子类MyDomain。唯一可以安全使用的类型是MyDomain - 但是,如果您打算仅使用MyDomain作为类型,那么您可以删除泛型类型绑定并仅返回MyDomain

+0

OP表示“调用工厂从不使用具体类” –

+0

@ sharonbn正确,但仍然不能安全;即使'''MyFakeDomain''是'''abstract''',它仍然会导致完全相同的问题。为了避免混淆,我会明确地将其抽象化。 – Toby

+0

这会和应该通过类抛出异常。当然你会使用'BarDomain'接口将代码与特定的实现分离开来。就像你宁愿'列表 = ...'而不是'ArrayList = ...' – Ian2thedv

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

+0

我认为这个解决方案将接受'FooDomain subj = SpecializedObjectsFactory.getFactory().create(BarDomain.class);'并且它会抛出一个classCastException –