Search code examples
javagenericsinterfacefactory-pattern

Java: Generics in interface and factory


I have a base class Plant from which there are two subtype paths. Fruit extends Plant and Vegetable extends Plant. Fruit and Vegetable are also abstract classes and then they further have other classes like Banana extends Fruit and Spinach extends Vegetable.

Now lets say for example, an operation has to be operation via these classes and there is an interface for the operation. The current design is as follows:

I have an interface:

abstract class Plant {}
abstract class Fruit extends Plant {}
class Banana extends Fruit {}

public interface PlantWeigher<T extends Plant> {    
    public int weight(T fruit);    
}

Then I have:

public abstract class FruitWeigher<Y extends Fruit> implements PlantWeigher<Y> { }

Then further I have the concrete class:

public class BananaWeigher extends FruitWeigher<Banana> {
  public int weight(Banana fruit);    
}

Now, as the user who just understands Plant, I would like to get the correct instance of the Weigher and then perform the operation.

So the Factory is as follows:

public class WeigherFactory {    
   public static PlantWeigher<? extends Plant> getInstance(Plant plant){
     // based on some logic on Plant argument, I return, lets say:
      return new BananaWeigher();    
   }
}

So in the user class, I call the following function:

PlantWeigher<? extends Plant> instance = WeigherFactory.getInstance(userPlant);
instance.weight(plant);  // THIS LINE GIVES AN ERROR

Could some one please help me understand what is wrong with the design here and also could some one please advice a solution.

Thanks.


Solution

  • The line :

     PlantWeigher<? extends Plant> instance = WeigherFactory.getInstance(userPlant);
    

    means, "I have a weigher that can weight some subclass of Plant, NOT all kinds of plants". For example, if the factory would return a BananaWeigher, it wouldn't make sense to try to weight a Tomato with this. To achieve what you want you should change the Factory as follows:

    public class WeigherFactory{
    
       public static <P extends Plant> PlantWeigher<P> getInstance(P plant){
        // should return a correct Weigher for the subttype of Plant P
                return ...
      }
    }
    

    Then the calling code would return be like:

     PlantWeigher<Banana> instance =WeigherFactory.getInstance(banana);
     instance.weigh(banana)
    

    Now you have a specific PlantWeigher and you can call it with a specific type

    OR, if you want to be able to weigh any kind of Plant with any kind of PlantWeigher, you should then change the latter:

     public interface PlantWeigher {
        public int weight(Plant p);
    }
    

    This ensures that a PlantWeigher can weight any plan, wether that makes sens or not.