Frank Haubenreisser Frank Haubenreisser - 1 year ago 144
Java Question

Return an object of child class from abstract base class

I have classes that I want to (de-)serialize. I don't want the code to appear in every class, so I thought I'll make child classes like this

public class Swan extends Animal {}


and a base class like this:

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


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

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


Basically I want this to work:

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


How do I do this if Animal is abstract? If the method looks like this:

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


I cannot return
new T(name) //parameter cannot be instantiated directly
. I read a bit about reflection but you cannot get the class of a generic type T. The workaround for this is to pass the class of the type into a constructor, but Animal should be abstract.

Answer Source

Due to type erasure you can't do exactly what you want, but here is some code that shows three options:

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

In order to instantiate T you have to have access to the Class object so you can do newInstance(). This requires a no-arg constructor or some more code to find and invoke the proper constructor, but that's basic reflection. You can hide the need for the Class if you provide a load method in Swan. Note that this is not an override, as inherited static methods don't participate in polymorphism. Swan.load merely hides the equivalent method from Animal.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download