In the method void add1(Room<? extends Animal> room)
, you define that the method accepts a Room
that holds an Animal
. For example, it can be Room<Cat>
, or Room<Dog>
--even Room<Animal>
for holding all types of animals. However, keep in mind that the room has been created outside this method call and you can't make any assumptions about the room type other than that it holds either a specific 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
But once you're inside the method, you can't know specifically which type of room had been passed.
It would be valid to call the method with a room for only birds add1(new Room<Bird>())
, as Bird
does indeed extend Animal
. However in the method body, you are adding a Dog
into it. That's why it's invalid, we can't put Dog
objects into Room<Bird>
. It is a Room
of some kind of animals and not a Room
of any kind of animals.
If you wanted to write a method that added a dog to a room appropriate for adding dogs (but not limited to just dog-only rooms), you'd write it with signature addDogToRoom(Room<? super Dog> room)
per this answer. This method could accept Room<Animal>
as well as Room<Dog>
and still within the method add new dogs to the room.
As about add4
, it's the same but the opposite. With Room<Cage<Animal>>
you specify that the method requires a specific room type--a room that allows only cages that hold any kind of Animal
. But then you are trying to put a Cage<Dog>
into it, a cage that allows only dogs. Therefore, it's invalid again.
Addition regarding comment:
Let's say there are cages designed for containing cats Cage<Cat>
and cages designed for containing dogs Cage<Dog>
. There are also universal cages, which can contain any kind of animal Cage<Animal>
. Those are three different kinds of cages, they can't be substituted for one another, as they have totally different architecture and design.
void method(Cage<Dog>)
means that the method needs one dog cage.
void method(Cage<Animal>)
means that the method needs one universal cage.
void method(Cage<? extends Animal>)
means that the method needs any kind of animal cage. Either dog cage, cat cage or universal cage.
Rooms are another level of abstraction--visualize them as a rooms with cages inside. There can be a room for storing cat cages Room<Cage<Cat>>
, a room for storing dog cages Room<Cage<Dog>>
, a room for storing universal cages Room<Cage<Animal>>
and a room for storing multiple kinds of animal cages Room<Cage<? extends Animal>>
. Therefore, the same rules apply:
void method(Room<Cage<Dog>>)
- room of dog cages
void method(Room<Cage<Cat>>)
- room of cat cages
void method(Room<Cage<Animal>>)
- room of animal cages
void method(Room<Cage<? extends Animal>>)
- room that can contain multiple kinds of animal cages. E.g., the room could simultaneously contain a Cage<Dog>
, and a Cage<Animal>
.
Now, in add3(Room<Cage<? extends Animal>> room)
, you request a last kind of room, the one that can contain "all kinds of animal cages". Therefore the room passed to the method can contain or add new dog cages room.add(new Cage<Dog>())
or any other type of cage.
However, to call that method, you would need to first create a new "universal" room (which supports all cages):
Room<Cage<? extends Animal>> room = new Room<Cage<? extends Animal>>();
add3(room);
Giving it a room of dog cages will not work:
// 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);
If you wanted to write a more flexible method that accepts rooms capable at minimum of holding dog cages, it could look like this:
void add(Room<Cage<? super Dog>> room) {
room.add(new Cage<Dog>());
room.add(new Cage<Animal>());
}