What type should an array store when using composition over inheritance?

89 Views Asked by At

When using inheritance, you can create two classes A and B that inherit from class C. You can then create an array of C to store either of these -- C[].

However, when using composition, what type would an array have to be to store either of these types?

class Entity {
    public int id;

    public Entity() {
        this.id = 0;
    }
}

class Player extends Entity {
    public Player() {
        this.id = 1;
    }
}

class Monster extends Entity {
    public Monster() {
        this.id = 2;
    }
}

public class Main {
    public static void main(String[] args) {
        Entity[] entities = new Entity[2];
 
        entities[0] = new Player(); // id == 1
        entities[1] = new Monster(); // id == 2
    }
}

When using composition, you'd have to store Entity as a field:

class Entity {
    public int id;

    public Entity() {
        this.id = 0;
    }
}

class Player {
    Entity entity;

    public Player() {
        this.entity = new Entity();
        this.entity.id = 1;
    }
}

class Monster {
    Entity entity;

    public Monster() {        
        this.entity = new Entity();
        this.entity.id = 2;
    }
}

public class Main {
    public static void main(String[] args) {
        Player player = new Player();
        Monster monster = new Monster();
        
        Entity[] entities = new Entity[2];
        // TODO: won't work! what type?
        entities[0] = player;
        entities[1] = monster;
    }
}
5

There are 5 best solutions below

0
Max Aliev On

You can only store entities in here. So just add player.entity/monster.entity or whatever.

0
mcyalcin On

The answer is already given as a comment to the question; you need to use a common superclass, which in this case is Object.

0
Mark Web On

When using composition, you would typically have to store objects of the specific class type directly, rather than an array of a common base class. In your example, you have Player and Monster classes composed of an Entity object, but you cannot store them in an array of Entity directly because Player and Monster do not inherit from Entity.

public static void main(String[] args) {
    Player player = new Player();
    Monster monster = new Monster();
    
    Entity[] entities = new Entity[2];
    entities[0] = player.entity;
    entities[1] = monster.entity;
}
0
R.Abbasi On

In your example, you used casting. Casting only works for "is a" or "inheritance" relationship. Using composition is not always recommended. The "favor composition over inheritance" statement means that don't overuse inheritance in every situation. But when you want to use features of inheritance like polymorphism for a real "is a" relationship like your example, there is no question, use it with no regrets. An example of wrong inheritance relationships would be like this:

Animal "is a" MoveableEntity => Animal "has a" MovingBehavior

0
Alpedar On

If interface of C is sufficient for everything you need to do with those items, use C[].

If you want to store A or B there and nothing else and handle each of them differently and not via their own methods, using some kind of composite type (eg. OneOf<T1,T2>) makes sense. This type then contains field for A and for B and exactly one of them is used and usually have (among other methods) method:

T Match<T>(Function<A,T>,Function<B,T>) 

which allows for exhaustive handling all possible types contained in this composite.