0

Here is the gist of what I am trying to do:

abstract class Animal {
    abstract static Animal fromInput(String input); // <- error

    static List<Animal> makeFive() {
        List<Animal> animals = new ArrayList<Animal>();
        animals.add(Animal.fromInput("Input 1"));
        animals.add(Animal.fromInput("Input 2"));
        animals.add(Animal.fromInput("Input 3"));
        animals.add(Animal.fromInput("Input 4"));
        animals.add(Animal.fromInput("Input 5"));
        return animals;
    }
    // ^^^ how to this rewrite this so Dog.makeFive() ^^^ //
    // ^^^ makes Dogs and Cat.makeFive() makes Cats?  ^^^ //
}

class Dog extends Animal {
    @Override static Dog fromInput(String input) {
        Dog dog = new Dog();
        // do some initial dog stuff with input
        return dog;
    }
}

class Cat extends Animal {
    @Override static Cat fromInput(String input) {
        Cat cat = new Cat();
        // do some initial cat stuff with input
        return cat;
    }
}

How to write this correctly?

I want to be able to call Dog.makeFive() to give me a List<Dog> or Cat.makeFive() to give me a List<Cat> without having to re-define makeFive() in Dog, Cat, and every other animal class.

EDIT: I know the use of static within an abstract class is wrong. My question is how to work around that so one is able to call Dog.makeFive() or Cat.makeFive() while only defining makeFive() once.

7

5 Answers 5

1

I want to be able to call Dog.makeFive() to give me a List or Cat.makeFive() to give me a List without having to re-define makeFive() in Dog, Cat, and every other animal class.

I feel your pain! It looks so much like it should be possible, but as the other answers and comments say, Java for whatever reason does not allow it.

So IMHO, the best workaround is to have the single makeFive method in Animal, and pass the destination class to it.

So your caller would be :

List<Dog> dogList = Animal.makeFive(Dog.class);

The implementation would be :

public abstract class Animal {

    public abstract void initialise(String input);

    static <T extends Animal> List<T> makeFive(Class<T> clazz) {
        List<T> animals = new ArrayList<T>();
        animals.add(makeNew(clazz, "Input 1"));
        animals.add(makeNew(clazz, "Input 2"));
        animals.add(makeNew(clazz, "Input 3"));
        animals.add(makeNew(clazz, "Input 4"));
        animals.add(makeNew(clazz, "Input 5"));
        return animals;
    }

    private static <T extends Animal> T makeNew(Class<T> clazz, String input) {
        T newAnimal;
        try {
            newAnimal = clazz.newInstance();
            newAnimal.initialise(input);
        } catch (InstantiationException | IllegalAccessException e) {
            newAnimal = null;  // Change This !!!  Maybe throw exception, or add "throws" to method declaration
        }
        return newAnimal;
    }

}

And finally, have each animal type perform it's initialisation :

class Dog extends Animal {

    @Override
    public void initialise(String input) {
        // do some initial dog stuff with input
    }
}

This approach does require that each subclass (Dog, Cat, etc) have a no-argument constructor, but that's a fairly common requirement for various objects in the Java ecosystem.

Alternatively, it could be possible to have the Animal.makeNew method use reflection to find an appropriate constructor, but I personally would advise against that since it becomes difficult to maintain.

Sign up to request clarification or add additional context in comments.

Comments

0
public abstract class Animal {

    abstract List<? extends Animal> makeFive();

}

class Dog extends Animal {

    @Override
    List<Dog> makeFive() {
        return null;
    }
}

class Cat extends Animal {

    @Override
    List<Cat> makeFive() {
        return null;
    }
}

2 Comments

This example requires defining makeFive() twice (once in Dog and once in Cat) but my question is can we somehow only define makeFive() once?
of course, you need twice as you need different implementation.
0

This would satisfy the requirements of your question:

public interface Animal<T> {

    List<T> makeFive();
}

And then Dog and Cat would both implement Animal

public class Dog implements Animal<DogType> {

   public Dog(){
      super();
   }


   @Override
   public List<DogType> makeFive(){
     // do stuff;
   }

}

public class Cat implements Animal<CatType> {

   public Cat(){
      super();
   }


    @Override
   public List<CatType> makeFive(){
     // do stuff;
   }
}

Alternatively, if you're using Java 8+ you can make an Animal an interface and define makeFive as a default method

public interface Animal<T> {
    List<T> makeFive();

}

4 Comments

I'm not seeing it. This would give me a List<Animal> but what I want is a List<Dog> or List<Cat>.
It sounds like you're asking about Generics. One moment
Also, how does one call makeFive() in your example? Do you have to instantiate a new object like new Dog().makeFive() just to be able to get the List<Dog> that don't really depend on the instantiated Dog?
It sounds like you need another method to keep track of adding animals...
0

You can remove the static keyword from the signature of the function and define the fromInput method as abstract --

abstract class Animal<T> {
    abstract T fromInput(String input);

    List<T> makeFive() {
        List<T> animals = new ArrayList<>();
        animals.add(fromInput("Input 1"));
        animals.add(fromInput("Input 2"));
        animals.add(fromInput("Input 3"));
        animals.add(fromInput("Input 4"));
        animals.add(fromInput("Input 5"));
        return animals;
    }
}

class Dog extends Animal<Dog> {
    @Override
    Dog fromInput(String input) {
        Dog dog = new Dog();
        // do some initial dog stuff with input
        return dog;
    }
}

class Cat extends Animal<Cat> {
    @Override
    Cat fromInput(String input) {
        Cat cat = new Cat();
        // do some initial cat stuff with input
        return cat;
    }
}

You can then use it to implement the invocation as follows --

public class Solution {

    public static void main(String[] args) {
        /*
         * Dog(s)
         */
        List<Dog> dogs = new Dog().makeFive();
        for (Dog dog : dogs) {
            System.err.println(dog.getClass());
        }

        /*
         * Cat(s)
         */
        List<Cat> cats = new Cat().makeFive();
        for (Cat cat : cats) {
            System.err.println(cat.getClass());
        }
    }

}

1 Comment

How do you call makeFive() in this example? By instantiating a new object such as new Dog().makeFive()? It seems incorrect to do it this way since the result of makeFive() does not depend on the particular instance of the new Dog.
0

A super class may not invoke instances of their sub classes unless they are explicitly declared in the super class. Otherwise the super classes don't even know about them. So for every instance of Dog or Cat that you want in the list, you need to add them from your sub class.

In your original design, you called makeFive once which contained a single instance of Dog or Cat. How would you expect the parent class of Animal to be able to instantiate different ones with different state?

Even if you had an interface to support this, each subclass would have to implement that interface to support its type.

But you already implement fromInput in each subclass so why not ignore that method and just implement (or override) makeFive()? It seems like you're implementing one method to keep from having to implement another.

Finally, there is one subtle problem with this. Unless you want List<Animal> to contain the same Dog or Cat object, you also need to copy the newly created List<Animal> to each Dog or Cat instance. Otherwise, they will be accessing an empty (and probably null) list.

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.