5

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?

2
  • yes it is not necessary that all the methods in a class need to be abstract Commented Sep 22, 2012 at 18:26
  • 1
    @SashiKant While true, I don't think that's what the OP was asking... Commented Sep 22, 2012 at 18:27

3 Answers 3

3

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( ... ));
        // ...
    }
}
Sign up to request clarification or add additional context in comments.

Comments

3

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.

4 Comments

Tried this, but it threw InstantiationException.
@Eng.Fouad did it throw it or did it say you needed to catch it? There are a number of examples of this working around the web.
Yes, it throws InstantiationException. You can try it yourself :)
If you want to avoid masking an exception thrown in the constructor, getClass().getConstructor().newInstance() is better, as it wraps any exception thrown in the constructor in an InvocationTargetException, just as when using invoke() on a regular method.
2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.