In java:
Cage<? extends Animal> animals = null;
It is a cage, but you do not know what animals it takes.
animals = lions; // Works!
Well, you do not add any opinion about what animals are in the cage, so the lion does not violate any expectations.
animals.add(new Lion());
You do not know what these cells are. In this particular case, it turns out to be a cage for lions in which you put a lion, but this is a rule that will allow this to simply allow any kind of animal to be placed in any cage. This is correctly prohibited.
In Scala: Cage[+T] : if B extends A , then a Cage[B] should be considered a Cage[A] .
Given that animals = lions allowed.
But is this different from java, a parameter of type definitely Animal , and not a wildcard ? extends Animal ? extends Animal . You are allowed to put an animal in Cage[Animal] , a lion is an animal, so you can put a lion in a [Animal] cage, which can be a [Bird] cage. This is pretty bad.
Except that it is actually not allowed (fortunately). Your code should not compile (if it was compiled for you, you noticed a compiler error). A covariant general parameter cannot be displayed as an argument to a method. The reason is that it would allow lions to be placed in a bird cage. It is displayed as +T in the Cage definition; it cannot be displayed as an argument to the add method.
Thus, both languages โโprohibit placing lions in bird cages.
Regarding your updated questions.
This is done because otherwise you can add a tiger?
Yes, this, of course, is the reason, the point of the type system is to make this impossible. Will this lead to an un runtime error? In all likelihood, this will happen at some point, but not at the moment when you call add, since the actual generic type is not checked at run time (erasure type). But the type system usually rejects every program for which it cannot prove that (some) errors will not happen, and not just a program where it can prove that they really happen.
Could there be a special (hypothetical) rule that would not reject it if only one subtype of Animal existed?
May be. Note that you still have two types of animals, namely Animal and Lion . Therefore, an important fact is that the Lion instance belongs to both types. On the other hand, an instance of Animal not of type Lion . animals.add(new Lion()) can be allowed (a cage or a cage for any animals, or only for lions, both are normal), but animals.add(new Animal()) should not (because animals can be a cage only for lions )
But in any case, it sounds like a very bad idea. The point of inheritance in an object-oriented system is that sometime later, someone else working somewhere else can add a subtype, and this will not lead to an incorrect system. In fact, the old code does not even need to be recompiled (perhaps you have no source). With this rule, this will not be true