friko
friko

Reputation: 607

Generic class initialization

This is my class:

public class FoodSet<T extends ConcreteFood>{
    public List<T> food = new ArrayList<T>();
    public FoodSet()
    {
        /*
        *  FoodType is an enum containing food types e.g: rice, pork, beef ...
        */
        for(FoodType foodType : FoodType.values())
        {  
            /*
            * 1(*)
            * /
            food.add( (T)new ConcreteFood(foodType,0) ) 
        }       
    }
}

And in 1(*) is the problem, how to init this list, with 'T' type ? Now I init it with ConcreteFood, giving as argument foodType and amount, but my goal is to init this list with T that extends ConcreteFood. Every subclass of ConcreteFood has access to foodType and food count. I just would to init this list with apropriate ConcreteFood subclass and init each ConcreteFood object with foodType and count = 0. How should I do it ?

Upvotes: 3

Views: 1134

Answers (3)

Vincent van der Weele
Vincent van der Weele

Reputation: 13177

In response to OP's answer: you really shouldn't want it this way. I would propose that you pass the correct concrete factory to your FoodSet as well.

Abstract factory:

public interface FoodFactory<T extends ConcreteFood> {
    public T create(FoodType type);
}

Concrete factory, one per subclass of ConcreteFood

public class BagFoodFactory implements FoodFactory<BagFood> {
    private static final BagFoodFactory INSTANCE = new BagFoodFactory ();

    private BagFoodFactory () {}

    public static FoodFactory<BagFood> getInstance() { return INSTANCE; }

    public BagFood create(FoodType type) {
        return new BagFood(type, 0);
    }
}

Use the factory in your FoodSet

public class FoodSet<T extends ConcreteFood> {
    public List<T> food = new ArrayList<T>();

    public FoodSet(FoodFactory<T> factory) {
        for (FoodType foodType : FoodType.values()) {
            food.add(factory.create(foodType));
        }
    }
}

Pass the correct factory to the constructor (you know which one you need here anyway).

class Sack
{
    public FoodSet<BagFood> bagFoodSet 
            = new FoodSet<BagFood>(BagFoodFactory.getInstance());
}

Upvotes: 1

OldCurmudgeon
OldCurmudgeon

Reputation: 65813

You could use your FoodType enum as a factory:

public static class ConcreteFood {
}

public static class Bacon extends ConcreteFood {
};

public static class SavoyCabbage extends ConcreteFood {
};

enum FoodType {
  Pork {
    @Override
    public ConcreteFood makeNew() {
      return new Bacon();
    }
  },
  Cabbage {
    @Override
    public ConcreteFood makeNew() {
      return new SavoyCabbage();
    }
  };

  public abstract ConcreteFood makeNew();
}

public static class FoodSet {
  public List<ConcreteFood> food = new ArrayList<ConcreteFood>();

  public FoodSet() {
    /*
     *  FoodType is an enum containing food types e.g: rice, pork, beef ...
     */
    for (FoodType foodType : FoodType.values()) {
      /*
       * 1(*)
       */
      //food.add((T) new ConcreteFood(foodType, 0)) 
      food.add(foodType.makeNew());
    }
  }
}

Upvotes: 1

duffymo
duffymo

Reputation: 308743

This isn't what you want. You need a factory class to create instances of ConcreteFood for you.

public class FoodFactory {
    private static final FoodFactory INSTANCE = new FoodFactory();

    private FoodFactory() {}

    public static FoodFactory getInstance() { return INSTANCE; }

    public Food create(FoodType type) {
       // Put the type checks here 
    }
}

I don't care for your naming much. "ConcreteFood"? Doesn't sound appetizing. Why not FoodCategory?

Upvotes: 1

Related Questions