简体   繁体   中英

Java Generic Container Class

I'm working on a evolutionary simulation model implemented in Java and ran into a key object-orientation design issue which I can't seem to work out. The problem can be summarized as follows:

I have a base abstract class Player and two concrete subclasses, Signaller and Receiver:

abstract class Player
{
    Strategy[] strategies;
    double fitness;
    ...
}

class Signaller extends Player
{
    double quality;
    ....
}

class Receiver extends Player
{
    double[] weights;
    int chosenChannel;
    ....
}

Now I need classes which represent collections of Signallers and Receivers and I am constrained to using arrays to store them. There are methods common to both population types, but also specific methods for a signaller populations or for a receiver population.

Conceptually , I would need something like this:

abstract class Population
{
    Player[] members;

    void mixUpPopulation() {...}
    Strategy[] getMeanStrategies() {...}
    double getMeanFitness() {...}
    ...
}

class SignallerPopulation extends Population
{
    Signaller[] members;
    ...
}

class ReceiverPopulation extends Population
{
    Receiver[] members;

    double[] getChannelPreferences() {...}
    ...
}

I have thought of two basic ways of achieving this:

  1. Have the class hierarchy as described above.
    Problem : How can the Player[] in the superclass and also the Signaller[] or Receiver[] in the subclasses refer to the same collection of objects?

  2. Make the base class generic:

class Population <T extends Player>
{
    ...    
    T[] members = (T[])new Object[popSize];
}

Problem : How do I implement the methods specific to each of the population types?

I would appreciate your insights into those problems or maybe suggestions of other ways of tackling the problem.

You can use the design 1 as in your question, but instead of storing an array in the abstract base class you add an abstract protected method (eg getMembers()) that will be implemented in the subclasses to return the actual array as an array of Players.

Alternatively, you can make the abstract base class generic, and derive the subclasses with the appropriate types:

abstract class Population<T extends Player>
{
    T[] members;

    void mixUpPopulation() {...}
    Strategy[] getMeanStrategies() {...}
    double getMeanFitness() {...}
    ...
}

class SignallerPopulation extends Population<Signaller>
{
    public SignallerPopulation(int popSize) { members = new Signaller[popSize]; }
    ...
}

class ReceiverPopulation extends Population<Receiver>
{
    public ReceiverPopulation(int popSize) { members = new Receiver[popSize]; }
    double[] getChannelPreferences() {...}
    ...
}

Remove the members from Population and add an abstract getter-method for members to it ( public abstract Player getMember(int i) and public abstract int getNumPlayers() or something similar). Subclasses are required to implement the getter. This way you will still have access to the Player part of XYPopulation members in Population .

I went with the design suggested by @Medo42 (his first option) and @LumpN. The only cast required was when setting the array, but that wasn't problematic. I am giving the outline of the code here, maybe someone will find it helpful.

abstract class Population
{
    protected abstract Player[] getMembers();  
    protected abstract void setMembers(Player[] members);    

    void mixUpPopulation() {...}
    Strategy[] getMeanStrategies() {...}
    double getMeanFitness() {...}
    ...
}

class SignallerPopulation extends Population
{
    Signaller[] members;

    protected Player[] getMembers() 
    {
        return this.members;
    }

    protected void setMembers(Player[] members) 
    {
        this.members = (Signaller[]) members; //required cast
    }
    ...
}

class ReceiverPopulation extends Population
{
    Receiver[] members;

    protected Player[] getMembers() 
    {
        return this.members;
    }

    protected void setMembers(Player[] members) 
    {
        this.members = (Receiver[]) members; //required cast
    }

    double[] getChannelPreferences() {...}
    ...
}

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