我有我想要(取消)序列化的课程。我不希望代码出现在每个类中,所以我想我会像这样制作子类
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")
如果Animal是抽象的,我该怎么做?如果方法如下所示:
public static <T extends Animal> T loadAnimal(String filename) {
//loadFromFile
}
我无法退回new T(name) //parameter cannot be instantiated directly
。我读了一些关于反射的内容,但你不能得到泛型类型的类T.这个的解决方法是将类型的类传递给构造函数,但Animal应该是抽象的。
答案 0 :(得分:2)
由于类型擦除,你无法完全按照自己的意愿行事,但这里有一些代码显示了三个选项:
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()
。这需要一个无参数构造函数或更多代码来查找和调用正确的构造函数,但这是基本的反映。如果您在Class
中提供load
方法,则可以隐藏对Swan
的需求。请注意,这不是覆盖,因为继承的静态方法不参与多态。 Swan.load
仅隐藏Animal
中的等效方法。
答案 1 :(得分: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;
}
答案 2 :(得分:0)
解决它的最简单方法 - 创建包含实例类型(特定动物类)和序列化主体的包装器。因此,您将首先加载类型,而不是实例化并阅读正确的类。
答案 3 :(得分:0)
There is no problem in declaring a return type that is an abstract class. That’s an important tool in Object Oriented Programming. E.g., the methods Arrays.asList(…)
, Collections.emptyList()
, Collections.unmodifiableList(…)
have in common that their return type is List
, an interface. This does not imply that List
in instantiated, as that’s impossible. It just implies that an unspecified subtype (implementation) of that interface is returned.
So you can simply declare your method to return Animal
and let it return whatever the deserialization produced, if casting the object to Animal
succeeded, it is a concrete subclass of Animal
, perhaps a Swan
.
If you want to remove the type cast at the caller side, you can provide the expected type as a parameter
public static <T extends Animal> T loadAnimal(String filename, Class<T> type) {
//loadFromFile
return type.cast(loadedObject);
}
which you can invoke as
Swan s2 = loadAnimal("/pathToSaveFile", Swan.class);
However, this does not provide more safety than an explicit type cast. There is no way to guaranty at compile time that a particular object loaded from an external file will contain a Swan
rather than an arbitrary other animal.
This assumed that you deserialized the entire Animal
instance, rather than properties of it. Otherwise, you could use the Class
object to create the proper instance, e.g. via newInstance
before reading its properties. But note that this is a questionable design. Normally, you create different subclasses, because they are going to have different properties.