1

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

Nicolas Zozol
  • 6,910
  • 3
  • 50
  • 74

2 Answers2

1

I think you make the wrong use of the Lower Bounded Wildcards. You use it as a turn around to the Upper Bounded Wildcards restrictions you may face by not specifying the Class of your wildcards in your implementation. I don't think you want to work with food and its supertypes. You only want to work with Food and its derivatives and you should find a solution with "? extends Food" or even get rid of the Wildcards and use List< Food >

Maaaatt
  • 429
  • 4
  • 14
1

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;
    }
}
Tobias Otto
  • 1,634
  • 13
  • 20
  • I have added another possible solution. – Tobias Otto Jul 20 '17 at 12:40
  • It works, I made just now a close solution where T and F extends Food : https://gist.github.com/nicolas-zozol/37748839e4bcfeaaf7cec914fb2442ce – Nicolas Zozol Jul 20 '17 at 12:45
  • But why `this.kitchen.build(bamboos);`compile ? How `void build(List super F> plate);` can accept bamboos when previously `void build(List super Food> plate);` did not accept these bamboos ? – Nicolas Zozol Jul 20 '17 at 12:46
  • I think my solution is better because you loose the ability to put anything on the plate in `void build(List plate);`. Using PECS, it should be void `build(List super F> plate);` – Nicolas Zozol Jul 20 '17 at 12:48