В Java существует понятие наследования и полиморфизма. Это означает, что если у нас есть класс Animal
, а классы Dog
и Cat
являются его наследниками, то мы можем считать, что Dog
и Cat
являются подтипами Animal
. Таким образом, где бы не требовался Animal
, мы можем использовать Dog
или Cat
.
Например, если у нас есть метод move(Animal animal)
, мы можем вызвать этот метод с объектами типов Dog
и Cat
, так как они являются подтипами Animal
.
Однако проблема возникает, когда мы начинаем работать с обобщениями (generics) в Java. Предположим, у нас есть метод checkAnimals(List<Animal> animals)
. Можно ли передать в этот метод List<Dog>
или List<Cat>
? Ведь Dog
и Cat
являются подтипами Animal
. Оказывается, что это невозможно.
List<Dog> dogs = new ArrayList<>(); checkAnimals(dogs); // ошибка компиляции
Причина этого кроется в том, как Java обрабатывает обобщения. В обобщениях полиморфизм не работает неявно, и List<Dog>
не считается подтипом List<Animal>
. Если бы это было так, то мы могли бы добавить Cat
в список dogs
, что ведет к противоречию и ошибкам во время выполнения.
List<Animal> animals = dogs; // если бы это было допустимо... animals.add(new Cat()); // ...то мы могли бы добавить Cat в список собак!
Чтобы обойти это ограничение и позволить передавать List<Dog>
и List<Cat>
в checkAnimals
, в Java используется конструкция List<? extends Animal>
. Это говорит компилятору, что мы можем передать список любого подтипа Animal
.
void checkAnimals(List<? extends Animal> animals) { /* ... */ } List<Dog> dogs = new ArrayList<>(); List<Cat> cats = new ArrayList<>(); checkAnimals(dogs); // теперь это работает checkAnimals(cats); // и это тоже
В этом случае мы все еще не можем добавить Cat
в список dogs
, потому что компилятор не знает точный тип, который используется вместо ?
.
Таким образом, полиморфизм в обобщениях Java работает несколько иначе, чем мы могли бы ожидать, и требует явного указания при использовании.
Добавить комментарий