Как использовать полиморфизм подтипов и обобщения для списков?

#generics #inheritance #java-8

#обобщения #наследование #java-8

Вопрос:

Я изучаю Java и прочитал о наследовании и полиморфизме подтипов. Сейчас я только начинаю изучать дженерики и пытаюсь собрать кусочки воедино, но застрял.

Допустим, у меня есть abstract родительский класс AbstractEdibles , и есть два дочерних, extend происходящих от него: Food и Drink . Кроме того, у меня есть a, Store у которого есть два List s: один для хранения Food и один для сохранения Drink .

Теперь я хочу, чтобы мой Store мог иметь поведение, такое как добавление к его запасу. Мой желаемый подход заключается в определении одного метода для получения продукта и помещения его в соответствующий список. Если это Food тип, внесите его в список продуктов. Если это тип Drink , поместите его в список напитков.

Я уверен, что мне нужно использовать дженерики и полиморфизм подтипов. Вот фрагмент моего кода:

 public class Store {

  private List<AbstractEdible> foodStock = new ArrayList<>();
  private List<AbstractEdible> drinkStock = new ArrayList<>();

  /**
   * A constructor for a new store inventory that has food and drink.
   *
   * @param foodStock   the list of food items in stock
   * @param drinkStock  the list of drink items in stock
   */
  public Store(List<AbstractEdible> foodStock, List<AbstractEdible> drinkStock) {
    this.foodStock = foodStock;
    this.drinkStock = drinkStock;
  }

  /**
   * Given an edible food, add it to its respective stock list.
   *
   * @param edible an edible item
   */
  public void addItemToStock(AbstractEdible edible) {
    if (product instanceof Food) {
      this.foodStock.add(product);
    } else if (product instanceof Drink) {
      this.drinkStock.add(product);
    }
  }
}
  

Это не приводит к ошибкам, но я недоволен тем, насколько свободно я обращаюсь с типами, и я думаю, что это можно было бы сделать более оптимальным.

Для начала я бы предпочел разрешить моему списку разрешать только этот конкретный тип edible . То есть, foodStock позволит Food только быть добавленным, и drinkStock позволит Drink только быть добавленным. То, что я пробовал, было:

 private List<AbstractEdible> foodStock = new ArrayList<Food>();
private List<AbstractEdiblet> drinkStock = new ArrayList<Drink>();
  

дает мне красное подчеркивание в моей IDE. Итак, затем я перешел на:

 private List<Food> foodStock = new ArrayList<Food>();
private List<Drink> drinkStock = new ArrayList<Drink>();
  

это работает, но затем мой addItemToStock() метод жалуется, что edible я пытаюсь добавить, имеет тип Edible и не может быть добавлен к List содержащему типу Food . (Это ожидаемо, потому что Edible является родителем Food ; т.е. не каждый Edible является Food , но каждый Food есть Edible .)

Приведение вверх / вниз edible тоже не помогает. Тогда я подумал, что подстановочные знаки с верхними границами — это то, что я искал, но это также не сработало.

Кроме того, я также не уверен, что использование instanceof является лучшим способом классификации / сортировки объектов.

Чего я здесь не понимаю? Какую концепцию я не понимаю?

Ответ №1:

Если вы хотите гарантировать, что только foodStock хранилища Food и только drinkStock stores Drink , тогда вам придется инициализировать свои списки следующим образом:

 private List<Food> foodStock = new ArrayList<>();
private List<Drink> drinkStock = new ArrayList<>();
  

Если они поступают из конструктора, просто объявите их:

 private List<Food> foodStock;
private List<Drink> drinkStock;
  

а затем назначить их в конструкторе:

 public Store(List<Food> foodStock, List<Drink> drinkStock) {
    this.foodStock = foodStock;
    this.drinkStock = drinkStock;
}
  

И после того, как вы использовали instanceOf , безопасно использовать приведение:

 public void addItemToStock(AbstractEdible product) {
    if (product instanceof Food) {
        this.foodStock.add((Food) product);
    } else if (product instanceof Drink) {
        this.drinkStock.add((Drink) product);
    }
}
  

Вы также могли бы создать универсальный Stock класс:

 public class Stock<T extends AbstractEdible> {

    private List<T> items;

    public Stock() {
        items = new ArrayList<>();
    }

    public void addItem(T item) {
        this.items.add(item);
    }

    public List<T> getItems() {
        return items;
    }
}
  

и используйте это в своем Store качестве:

 public class Store {
    private Stock<Food> foodStock;
    private Stock<Drink> drinkStock;

    public Store() {
        this.foodStock = new Stock<>();
        this.drinkStock = new Stock<>();
    }

    public Store(Stock<Food> foodStock, Stock<Drink> drinkStock) {
        this.foodStock = foodStock;
        this.drinkStock = drinkStock;
    }

    public void addItemToStock(AbstractEdible product) {
        if (product instanceof Food) {
            this.foodStock.addItem((Food) product);
        } else if (product instanceof Drink) {
            this.drinkStock.addItem((Drink) product);
        }
    }
}
  

Комментарии:

1. Хм, видимо, я был близок к этому в первый раз. Я попробовал кастинг, но, думаю, с первого раза я сделал это неправильно, так как это не сработало. Но ваш (первый) метод сработал, спасибо. И 1 за то, что также включен второй подход.