2016-07-11 107 views
1

我有我想(去)序列化的類。我不希望出現在每一類中的代碼,所以我想我會子類這樣從抽象基類返回子類的對象

public class Swan extends Animal {} 

和基類是這樣的:

public abstract class Animal { 
    protected String name; 
    // ... 


    public void saveAnimal(String filename) { 
     //ObjectOutputStream, save name... 
    } 

    public static /*returntype*/ loadAnimal(String filename) { 
     //ObjectInputStream... 
    } 
} 

基本上我想這工作:

Swan s1 = new Swan("name"); 
s1.saveAnimal("/pathToSaveFile"); 
Swan s2 = (Swan)loadAnimal("/pathToSaveFile") OR 
Swan s2 = loadAnimal("/pathToSaveFile") 

我怎麼做,如果動物是抽象的?如果方法如下所示:

public static <T extends Animal> T loadAnimal(String filename) { 
    //loadFromFile 
    } 

我不能返回new T(name) //parameter cannot be instantiated directly。我讀了一些關於反射的東西,但是你不能得到泛型類型T的類。解決方法是將類型的類傳入構造函數,但Animal應該是抽象的。

+0

請問這個問題有點幫助你嗎?:http://stackoverflow.com/questions/20638749/chaining-returning-base-objects-and-type-mismatch-to-extended-classes – Adam

回答

1

由於類型擦除你不能做你想要什麼,但這裏是一些代碼,顯示了三個選項:

public class Foo { 

    public static class Animal { 
     public void save(String filename) 
     { 
      // Write to file 
     } 
     public static <T extends Animal> T load(String filename, Class<T> cls) throws Exception 
     { 
      T result = cls.newInstance(); 
      // initialize result 
      return result; 
     } 
    } 

    public static class Swan extends Animal { 
     public static Swan load(String filename) throws Exception 
     { 
      return load(filename, Swan.class); 
     } 
    } 

    public static void main(String[] args) throws Exception 
    { 
     Swan s = new Swan(); 
     s.save("somefile"); 
     Swan s2 = Swan.load("somefile", Swan.class); 
     // OR equivalently 
     Swan s3 = Animal.load("somefile", Swan.class); 
     // OR also equivalent 
     Swan s4 = Swan.load("somefile"); 
    } 
} 

爲了實例T你必須有訪問Class對象,你可以做newInstance()。這需要一個無參數的構造函數或一些更多的代碼來查找和調用正確的構造函數,但這是基本的反射。如果您在Swan中提供load方法,則可以隱藏對Class的需求。請注意,這不是重寫,因爲繼承的靜態方法不參與多態。 Swan.load僅僅隱藏了從Animal的等效方法。

+0

好的,所以我仍然要通過天鵝班......作爲一個論點。我認爲還有另外一種方法,比如創建一個對象,然後類型轉換它或類似的東西。謝啦! –

+0

是的。你可以用第三個選項來獲得你想要的結果,這會掩蓋'Swan.load'中'Class'對象的需求。 –

0

如果你這樣做,然後檢查實例,然後它會工作...

public static Animal loadAnimal(String filename) { 
    //ObjectInputStream... 
} 

例子:

Swan s1 = new Swan("name"); 
s1.saveAnimal("/pathToSaveFile"); 
Animal s2 = loadAnimal("/pathToSaveFile") //it is ok since Swan is an animal.. 
if(s2 instanceOf Swan){ 
    Swan sccc = (Swan)s2; 
} 
+0

是的,但我不' t要動物被實例化。這就是爲什麼我說動物類是抽象的。無論如何:-)謝謝 –

+1

@FrankHaubenreisser動物我沒有在這個答案中實例化。它只是一種類型。 – LeTex

0

解決這個問題的最簡單方法 - 創建包裝包含實例類型(特定動物類)和序列化機構。因此,您將首先加載類型,而不是實例化並閱讀正確的類。

+0

爲什麼使用'Serializable'有必要這樣做?不應該數據已經包含類型信息? –

0

聲明返回類型是抽象類沒有問題。這是面向對象編程中的一個重要工具。例如,方法Arrays.asList(…),Collections.emptyList(),Collections.unmodifiableList(…)的共同之處在於它們的返回類型是List,即接口。這並不意味着List在實例化,因爲這是不可能的。這隻意味着返回該接口的未指定子類型(實現)。

因此,你可以簡單地聲明你的方法返回Animal,讓它返回任何反序列化生產,如果鑄造的對象Animal得手,這是一個Animal具體子類,也許是Swan

如果你想刪除的主叫側投的類型,可以提供預期的類型作爲參數

public static <T extends Animal> T loadAnimal(String filename, Class<T> type) { 
    //loadFromFile 
    return type.cast(loadedObject); 
} 

,你可以調用作爲

Swan s2 = loadAnimal("/pathToSaveFile", Swan.class); 

然而,這並不比明確的類型轉換提供更多的安全性。編譯時無法保證從外部文件加載的特定對象將包含Swan而不是任意其他動物。

這假定你反序列化了整個Animal實例,而不是它的屬性。否則,您可以使用Class對象創建適當的實例,例如通過newInstance閱讀其屬性之前。但請注意,這是一個值得懷疑的設計。通常,您創建不同的子類,因爲它們將具有不同的屬性。