tags:

views:

52

answers:

3

Say you have a class Population and Population makes an internal List of Individuals. The user wants to pass an unconstructed Individual subclass to Population and let Population do the work of constructing these, whatever subclass was passed.

  public class Population{

    private List<Individual> indiList = new ArrayList(25);


    /*I know this won't work for multiple reasons, but shows the idea*/
    public void addIndividualsOfType(Individual individual){

    for (ListIterator it = indiList.listIterator(); it.hasNext(); ){
      it.next()
      it.add(individual.construct(param1, param2, param3);  
    }
  }

}

  public abstract class Individual{
  SomeType param1;
  ...

  public Individual(SomeType param1, SomeType param2, SomeType param3){
    this.param1 = param1;
    ...
  }
}

  public class HappyIndividual extends Individual{
   ...
  }

  public class SadIndividual extends Individual{
   ...
  }

I would like to be able to make a call like population.addIndividualsOfType(HappyIndividual)

Obviously, this code will not work for many reasons (like passing an uninstantiated class). I am just trying to illustrate the idea of what I want to do, this is not a question of why the above code does not work. I know it won't work, which is why I can't figure out how one would do such a thing.

I have been messing around with Builders and Static Factory Methods all day. The problem is that we are talking subclasses here. Subclasses don't play well with Static. If I give Individual a static factory method, then I end up creating Individuals, not HappyIndividuals. Similar problems occur with Builder. I also want to keep this not overly complex.

I feel like this should be very easy, but I'm having trouble. Thanks.

+1  A: 

I would like to be able to make a call like population.addIndividualsOfType(HappyIndividual)

You were quite close when you were asking to do that. You can instead pass HappyIndividual.class, which is the class object representing the HappyIndividual class. This object itself is of type Class which represents a class at run-time.

Your method would then look like this:

public void addIndividualsOfType(Class<? extends Invididual> clazz) {
    for (ListIterator it = indiList.listIterator(); it.hasNext(); ){
      it.next();
      it.add(clazz.newInstance());
    }
}

The call to newInstance assumes that your class has a default constructor without parameters. Otherwise, you need to use the getConstructor method to obtain a specific constructor and then call newInstance on that constructor with the correct parameters.

casablanca
Beat me to it... +1 for `Class<? extends Invididual> clazz` instead of `addIndividualsOfType(Class clazz)`
Tony Ennis
A: 

Pass in the class in question (e.g., HappyPerson.class) and get the constructor by calling Class.getConstructor(arg...), then instantiate the instance.

Steve Emmerson
+1  A: 

There are essentially two ways to do this:

You could continue down your current path and use reflection to construct the classes:

public void addIndividualsOfType(Class<? extends Individual> clazz){
    for (ListIterator it = indiList.listIterator(); it.hasNext(); ){
        it.next()
        it.add(clazz.newInstance());  // using no-args constructor
    }
}

and to use a constructor with arguments you'd replaceit.add(clazz.newInstance()) with something like this:

    Constructor<? extends Individual> cons = clazz.getConstructor(
        param1.getClass(), param2.getClass(), param3.getClass());
    it.add(cons.newInstance(param1, param2, param3));

(I've left out the exception handling ... but you get the picture.)

However, an altogether better approach would be to refactor your code so that you passed in a factory object rather than a Class object. For example:

public interface IndividualFactory {
    public Individual create(T1 p1, T2 p2, T3 p3);
}

public static final IndividualFactory TALL_FACTORY = new IndividualFactory() {
    public Individual create(T1 p1, T2 p2, T3 p3) {
        return new TallIndividual(p1, p2, p3);
    }
};

public void addIndividualsOfType(IndividualFactory factory){
    for (ListIterator it = indiList.listIterator(); it.hasNext(); ){
        it.next()
        it.add(factory.create(param1, param2, param3);
    }
}

Note that the factory-based solution can be statically type-checked. By contrast, the reflective version has to deal with a number of type-related runtime exceptions.

Stephen C
I know Factories are recommended over reflection. Why?
Ben B.
@Ben B - factories are more robust than a reflection-based solution since they are statically type-checked. If there are type related bugs they are likely to be picked up at compile time. Reflection is also slower, since the type checking is performed repeatedly at runtime, rather than once at compile time.
Stephen C
There is actually something very wrong with your code. I think I got what you were trying to write... but you might want to clean that up.
Ben B.
Oh, I think I see what you were trying to do. I was confused because you have some extra brackets.
Ben B.
@Ben B - I don't know what you are talking about. Please explain.
Stephen C
I think I just learned something new about Java. Please explain how these anonymous inner functions work. I have read a lot about java, and never heard of them or seen them.
Ben B.