Search code examples
javapolymorphismpecs

PECS: How to convert a Consumer to a Producer?


I have a Restaurant that produces Meals. The kitchens is given plates that are Consumers.

class Food{}
class Bamboo extends Food {}

interface Kitchen {
    void build(List<? super Food> dessert);
}

abstract class Restaurant {
    Kitchen kitchen;

    public Restaurant(Kitchen kitchen) {
        this.kitchen = kitchen;
    }

    List<? extends Food> getMeals() {
        List<Food> food = new ArrayList<>();
        this.kitchen.build(food);
        return food;
    }
}

class PandaKitchen implements Kitchen{

    // Exact signature of the Kitchen
    @Override
    public void build(List<? super Food> plate) {
        // the List IS a consumer of bamboos
        plate.add(new Bamboo());
    }
}

// Bamboo specialized restaurant
class OhPanda extends Restaurant {

    public OhPanda() {
        super(new PandaKitchen());
    }

    // Specialized signature of List<? extends Food>
    @Override
    List<Bamboo> getMeals() {
        List<? super Food> bamboos = new ArrayList<>();
        this.kitchen.build(bamboos);

        // Obviously here, there is no information of having only Bamboos
        return bamboos; // <==== FAIL

        //return (List<Bamboo>) bamboos; // would not compile
    }
}

In the last line, I know that my OhPanda Restaurant only produces Bamboos. What is the best practice to convert my List<? super Food> without creating/copying an ArrayList in memory ?

A more complete Gist is written here: https://gist.github.com/nicolas-zozol/8c66352cbbad0ab67474a776cf007427


Solution

  • Or, maybe you could write a typed version of the restaurant and kitchen?

    package kitchen;
    
    import java.util.ArrayList;
    import java.util.List;
    
    class Food{}
    class Bamboo extends Food {}
    
    interface Kitchen<F> {
        void build(List<F> dessert);
    }
    
    abstract class Restaurant<T> {
        protected Kitchen kitchen;
    
        Restaurant(Kitchen kitchen) {
            this.kitchen = kitchen;
        }
    
        List<T> getMeals() {
            List<T> food = new ArrayList<>();
            kitchen.build(food);
            return food;
        }
    }
    
    class PandaKitchen implements Kitchen<Bamboo>{
    
        @Override
        public void build(List<Bamboo> dessert)
        {
            dessert.add(new Bamboo());
        }
    }
    
    /** Bamboo specialized restaurant*/
    class OhPanda extends Restaurant<Bamboo> {
    
        OhPanda() {
            super(new PandaKitchen());
        }
    
        @Override
        List<Bamboo> getMeals() {
            List<Bamboo> bamboos = new ArrayList<>();
            kitchen.build(bamboos);
            return bamboos;
        }
    }