That's because of "type erasure": the types are used by the compiler at compile-time to check for errors, but thereafter are "erased", i.e. no information about the generic types exists at runtime (for backwards compatibility with pre-generics Java code). Thus, at runtime, the only thing that's known is that you passed in a List to the method, and you're casting a List to a List. That will work. You might run into trouble when you start to work with items from the list though (since you're expecting them to be Dogs but they are Cats). Check the Generics tutorial for more info on type erasure.

The problem is not detected at compile time, since all separate steps are valid:
- List<Cat> is a List<? extends Animal>
- List<Dog> is a List<? extends Animal>