2016-07-11 34 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阅读其属性之前。但请注意,这是一个值得怀疑的设计。通常,您创建不同的子类,因为它们将具有不同的属性。