简体   繁体   中英

Creating new object in abstract class in Java

I have two objects which use really similar methods, save for one line. For example:

public class Cat extends Animal
public class Dog extends Animal

And they both use a breed method in the abstract class Animal . One calls new Dog() , and the other new Cat() . Right now I just have it declared as abstract public void breed(); in Animal, but is there a way I can generalize it so I don't have to make it an abstract method to be overridden?

Actually, yes you can. You need to use reflection so performance could be a little iffy, but this (untested) should work:

public abstract class Animal{
    public Animal breed(){
      return getClass().newInstance();
    }
    //other methods
}

This will return a new instance of the actual calling type, not the type of Animal (where it's implemented).

This is actually somewhat similar to the Prototype Pattern . Although in this case you're creating a new instance, not copying an existing instance.

Edit

As @FrankPavageau pointed out in the comments, rather than masking an exception in the constructor, you can achieve the same result by using

public abstract class Animal{
    public Animal breed(){
      return getClass().getConstructor().newInstance();
    }
    //other methods
}

Which will wrap any exception thrown in an InvocationTargetException which is a bit cleaner and probably easier to debug. Thanks @FrankPavageau for that suggestion.

There are many ways to do this, assuming by breed you mean "create children of me."

Reflection

First is to use reflection. If you have a no-args constructor for your classes, this is as easy as calling Class.newInstance :

public Animal breed() {
    try {
        return (Animal) getClass().newInstance();
    } catch (Exception ex) {
        // TODO Log me
        return null;
    }
}

If you don't have a no-args constructor in all your subclasses, you'll have to have a uniform constructor across all your subclasses. For example, if you have Cat(int, String) and Dog(int, String) , then you need to get the constructor via Class.getConstructor and invoke newInstance on that:

return (Animal) getClass().getConstructor(int.class, String.class).newInstance(0, "Unnamed");

int and String here may be age and name, for example. This is how you do this with reflection.

Providers

Another way is to use this simple interface:

public interface Provider<T> {
    T create();
}

Then have your abstract class take an instance of this in its constructor:

public abstract class Animal {
    private final Provider<Animal> animalProvider;

    protected Animal( ... , Provider<Animal> animalProvider) {
        // ...
        this.animalProvider = animalProvider;
    }

    public Animal breed() {
        return animalProvider.create();
    }
}

Then your subclasses will pass a Provider<Animal> to the superclass which will create new instances of the subclass:

public class Dog extends Animal {
    public Dog( ... ) {
        super( ... , new DogProvider());
        // ...
    }

    private static class DogProvider implements Provider<Animal> {
        public Animal create() {
            return new Dog( ... );
        }
    }
}

Do the same for other subclasses as well.

Note: if by breed you mean "get the type of me," then you should edit your question to say so. If this is what you meant, then this is a viable solution:

public abstract class Animal {
    protected final Breed breed;

    protected Animal( ... , Breed breed) {
        // ...
        this.breed = breed;
    }

    public Breed getBreed() {
        return breed;
    }
}

I recommend following the get / set conventions for data container methods. Java has bean classes designed to handle these naming conventions, and it's more or less a standard across many platforms. For your subclasses:

public class Dog extends Animal {
    public Dog( ... ) {
        super( ... , new Breed( ... ));
        // ...
    }
}

No there isn't. You will have to have something like what you have done as below

I think you want to have in your abstract class

public abstract Breed getBreed();

and then in each sub class have

public Breed getBreed() {
    return new DogBreed();
}

and a similar one returning cat.

or

Have a protected field in the Animal class called breed. This could then be initialised in each of the subclasses. This would remove the need for an abstract method. For example

public abstract class Animal {
    Breed breed;
...
}

and then in Dog have

public class Dog extends Animal {
    public Dog() {
        breed = new DogBreed();
    }
}

and have something similar to Cat.

It might be worth you while also passing in the breed to the Dog/Cat ctor so that you can create Dog objects of different breeds rather than restricting your model to just one breed of Dog

I am not sure Breed is necessarily modelled correctly in your example. Do you really want new Dog() to be a breed? Or do you mean type? In which case it is just an animal and the abstract method returning animal is the way to go.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM