2015-06-04 225 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 –