В методе void add1(Room<? extends Animal> room)
вы определяете, что метод принимает Room
, который содержит Animal
. Например, это может быть Room<Cat>
или Room<Dog>
- даже Room<Animal>
для содержания всех типов животных. Однако имейте в виду, что комната была создана вне этого вызова метода, и вы не можете делать какие-либо предположения о типе комнаты, кроме того, что она содержит либо конкретного животного.
add1(new Room<Dog>()); // give the method a room for dogs
add1(new Room<Cat>()); // give the method a room for cats
add1(new Room<Animal>()); // give the method a room for any animal
Но как только вы попадаете в метод, вы не можете точно знать, какой тип комнаты был пройден.
Было бы правильно вызывать метод с комнатой только для птиц add1(new Room<Bird>())
, поскольку Bird
действительно расширяет Animal
. Однако в теле метода вы добавляете Dog
в него. Вот почему это неверно, мы не можем поместить Dog
объектов в Room<Bird>
. Это Room
из некоторых видов животных, а не Room
из любых видов животных.
Если вы хотите написать метод, который добавляет собаку в комнату, подходящую для добавления собак (но не ограничивается только комнатами только для собак), вы должны написать это с подписью addDogToRoom(Room<? super Dog> room)
за этот ответ . Этот метод может принимать Room<Animal>
, а также Room<Dog>
и все еще в рамках метода добавлять новых собак в комнату.
Что касается add4
, то же самое, но наоборот. С Room<Cage<Animal>>
вы указываете, что метод требует определенного типа комнаты - комнаты, в которой разрешены только клетки с любым видом Animal
. Но затем вы пытаетесь поместить в нее Cage<Dog>
клетку, которая позволяет только собак. Следовательно, он снова недействителен.
Дополнение относительно комментария:
Допустим, есть клетки, предназначенные для содержания кошек Cage<Cat>
, и клетки, предназначенные для содержания собак Cage<Dog>
. Существуют также универсальные клетки, в которых могут содержаться любые виды животных Cage<Animal>
. Это три разных типа клеток, они не могут заменить друг друга, так как имеют совершенно разную архитектуру и дизайн.
void method(Cage<Dog>)
означает, что для метода требуется одна клетка для собаки.
void method(Cage<Animal>)
означает, что для метода требуется одна универсальная клетка.
void method(Cage<? extends Animal>)
означает, что для этого метода требуется любая клетка для животных. Либо клетка для собаки, клетка для кошки или универсальная клетка.
Комнаты - это еще один уровень абстракции - визуализируйте их как комнаты с клетками внутри. Может быть комната для хранения клеток для кошек Room<Cage<Cat>>
, комната для хранения клеток для собак Room<Cage<Dog>>
, комната для хранения универсальных клеток Room<Cage<Animal>>
и комната для хранения нескольких видов клеток для животных Room<Cage<? extends Animal>>
. Следовательно, применяются те же правила:
void method(Room<Cage<Dog>>)
- комната с собачьими клетками
void method(Room<Cage<Cat>>)
- комната с клетками для кошек
void method(Room<Cage<Animal>>)
- комната с клетками для животных
void method(Room<Cage<? extends Animal>>)
- комната, которая может содержать несколько видов клеток для животных. Например, комната может одновременно содержать Cage<Dog>
и Cage<Animal>
.
Теперь, в add3(Room<Cage<? extends Animal>> room)
, вы запрашиваете последний тип помещения, в котором могут содержаться «всевозможные клетки для животных». Поэтому комната, переданная методу, может содержать или добавлять новые клетки для собак room.add(new Cage<Dog>())
или клетки любого другого типа.
Однако, чтобы вызвать этот метод, вам нужно сначала создать новую "универсальную" комнату (которая поддерживает все клетки):
Room<Cage<? extends Animal>> room = new Room<Cage<? extends Animal>>();
add3(room);
Предоставление комнаты с собачьими клетками не сработает:
// Here we create a room that can contain only dog cages
Room<Cage<Dog>> room = new Room<Cage<Dog>>();
// But the method needs a "any kind of animal cage" room
// Therefore we get error during compilation
add3(room);
Если вы хотите написать более гибкий метод, который принимает комнаты, способные как минимум содержать клетки для собак, он может выглядеть следующим образом:
void add(Room<Cage<? super Dog>> room) {
room.add(new Cage<Dog>());
room.add(new Cage<Animal>());
}