The compiler doesn't prevent code from casting a type to an interface, unless it can establish for sure that the relationship is impossible.
If the target type is an interface, then it makes sense because a class extending Foo
can implement Map
. However, note that this only works as Foo
is not final
. If you declared your class with final class Foo
, that cast would not work.
If the target type is a class, then in this case it would simply fail (try (HashMap) this
), because the compiler knows for certain that the relationship between Foo
and HashMap
is impossible.
For reference, these rules are described in JLS-5.5.1 (T = target type - Map
, S = source type - Foo
)
If T [target type] is an interface type:
If S is not a final class (§8.1.1), then, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs.
Otherwise, the cast is always legal at compile time (because even if S does not implement T, a subclass of S might).
If S is a final class (§8.1.1), then S must implement T, or a compile-time error occurs.
Note the bold-italic comment in the quoted text.