ToMakPo
ToMakPo

Reputation: 864

Java: Static Parent/Child variables and methods

I am trying to create a parent class in Java that passes static variables to each of it's child classes so that each of the child classes has its own copy.

Here is an example of the code:

import java.util.*;

public class JavaFiddle {
    public static void main(String[] args) {
        Animal rex = new Dog("Rex", 3.2, 'M');
        Animal luna = new Dog("Luna", 1.2, 'M');
        Animal sadie = new Dog("Sadie", 0.1, 'F');
        Animal daisy = new Dog("Daisy", 5.9, 'F');
        Animal snowball = new Cat("Snowball", 3.8, 'M');
        Animal tiger = new Cat("Tiger", 9.8, 'M');

        System.out.println(Dog.getCount()); // output => 4
        System.out.println(Cat.getCount()); // output => 2
    }
}

class Animal {
    protected static final List<Animal> list = new ArrayList<>(); // Each child class should get it's own static list

    String name;
    double age;
    char gender;


    Animal (String name, double age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;

        list.add(this); // This should add the child object to the the static list in the child class
    }

    public static int getCount() { // This should return the size of the static list for the child class
        return list.size();
    }
}

class Dog extends Animal {
    protected static final List<Dog> list = new ArrayList<>(); // I don't want to have to add this for each child class. It should just get a copy from the parrent.

    Dog (String name, double age, char gender) {
        super(name, age, gender);

        list.add(this); // I don't want to have to add this for each child class. It should just be added from the parrent.
        // other stuff
    }

    public static int getCount() { // I don't want to have to add this for each child class. It should just get a copy from the parrent.
        return list.size();
    }
}

class Cat extends Animal {
    protected static final List<Cat> list = new ArrayList<>(); // I don't want to have to add this for each child class. It should just get a copy from the parrent.

    Cat (String name, double age, char gender) {
    super(name, age, gender);

        list.add(this); // I don't want to have to add this for each child class. It should just be added from the parrent.
        // other stuff
    }

    public static int getCount() { // I don't want to have to add this for each child class. It should just get a copy from the parrent.
        return list.size();
    }
}

I don't want to have to recreate each of these classes and variables inside of each of the child classes. That would kind of defeat the purpose of having the parent.

Is this even possible or am I going to have to build it for each one? I think I might be able to do it with the <T> thing like the way they do the List<T> ... but I don't know enough about it or even what that is called.

Any help would be great.

Upvotes: 0

Views: 146

Answers (3)

GhostCat
GhostCat

Reputation: 140641

The technical solution how to do that is outlined in the other answer.

But beyond that, I have a distinct non-answer here: don't do this!

Assuming that we are talking about "real world" OO design and best practices - then the code shown in the question would give material for hours of extensive "why to not do OO like this" discussions:

static is more of an abnormality in good OOP. So is the idea of exposing fields to subclasses. The key thing in OOP is polymorphism - the ability to change behaviour by subclassing and overriding methods. As soon as you start using static say goodbye to polymorphism. And then: fields are an implementation detail - they should not be used by other classes.

If you ignore these basic rules you end up with code that is tightly coupled all over the place. The idea of using different classes is to build independent units that can work in isolation as much as possible.

Your approach boils down to the exact opposite of that. Chances are that you will be unable to make single changes in one place - as you most likely will have to change other places most of the time.

In that sense: step back and look again at the problem you intend to solve. And then come up with a design that follows good OOP practices - instead of negating them.

To give some more actual suggestions: on a first glance, it might look very convenient to have a static list inside your classes that count the number of instances. But this is a direct violation of the Single Responsibility Principle. You see, real "business logic objects should have a very distinct purpose. You create classes, and instantiate objects of these classes because it makes sense in your model. And your model, is well, a model of the "real world" thing you intend to represent. Coming from there: does a dog or cat know about other dogs or cats? Not really. And so should be your code. If you have good reasons to keep "lists of instantiated objects" - then use a specific class for that. You are trying to make Animals also a List-of-Animals-manager. And in order to implement that you have zero need for using static anyway. You could use a factory to create objects, and that factory could make use of that ListKeeper implementation. And just for the record: you understand that simply keeping references in a list like this causes a memory leak? Because these objects can never turn into garbage, therefore you just keep adding new objects...

So, again: you don't need or want that static inherited list - because in a good OO model, you would have a distinct class responsible for managing such things.

Imagine for example that at one point that single list doesn't work any more. Maybe you want to organize different dogs in different lists? That means you have to update the Dog class - although "organizing Dogs in different lists" has nothing to do with the Dog class itself.

And then you want your classes to be open for extension but closed for modification. In your case, that would rather look like this:

public abstract class Animal {
  private final String name;

  public Animal(String name) { 
    this.name = Objects.requireNonNull(name, "name must not be null"); }

  private final String getName() { return name; }

  public abstract foo();
  public abstract String getEnhancedName();

In other words: you only use protected and "field inheritance" if there are good reasons to do so. Because, as said: fields are implementation details - and no other class (either your children) should know about such things. Or do you make your credit card available to your children? No - if at all you provide a "getter" for that which allows you to keep a certain amount of control. You don't just put your wallet on the table and tell your kids: "now go and use my credit card as you want to".

Upvotes: 1

Adrian
Adrian

Reputation: 3134

You can use a map in Animal

public class Animal {
  private static final Map<Class, List<Animal>> map = new HashMap<>();

  Animal (String name, double age, char gender) {
    //...
    map.computeIfAbsent(this.getClass(), key -> new ArrayList<>()).add(this);
  }

  public static int getCount(Class clazz) {
    return map.get(clazz).size();
  }
}

public class Dog extends Animal {
   Dog(String name, double age, char gender) {
      super(name, age, gender);
   }
   //optional
   public static int getCount() {
     return Animal.getCount(Dog.class);
  }
}

And in main you'll have:

System.out.println(Animal.getCount(Dog.class)); // output => 4
System.out.println(Animal.getCount(Cat.class)); // output => 2

//or
System.out.println(Dog.getCount()); // output => 4
System.out.println(Cat.getCount()); // output => 2

Upvotes: 0

RobOhRob
RobOhRob

Reputation: 585

import java.util.*;

public class Animal {

    protected static final List<Animal> list = new ArrayList<>(); // Each child class should get it's own static list

    String name;
    double age;
    char gender;

    Animal(String name, double age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;

        list.add(this); // This should add the child object to the the static list in the child class
    }

    public static int getCount() { // This should return the size of the static list for the child class
        return list.size();
    }
}

class Dog extends Animal {
    Dog(String name, double age, char gender) {
        super(name, age, gender);

    }
}

class Cat extends Animal {   
    Cat(String name, double age, char gender) {
        super(name, age, gender);
    }

    public static void main(String[] args) {
        Animal rex = new Dog("Rex", 3.2, 'M');
        Animal luna = new Dog("Luna", 1.2, 'M');
        Animal sadie = new Dog("Sadie", 0.1, 'F');
        Animal daisy = new Dog("Daisy", 5.9, 'F');
        Animal snowball = new Cat("Snowball", 3.8, 'M');
        Animal tiger = new Cat("Tiger", 9.8, 'M');

        for(Animal anim : Animal.list){
            if(anim instanceof Dog){
                System.out.println("Dog");
            }else if(anim instanceof Cat){
                System.out.println("Cat");
            }
        }
    }
}

This outputs: Dog Dog Dog Dog Cat Cat

Upvotes: 0

Related Questions