You can always do:
static class InterestingType extends ClassB implements InterfaceC {
}
And then have :
public static <T extends InterfaceC> T getInterfaceC2() {
return (T)new InterestingType();
}
As such calling:
ClassB clazzB3 = getInterfaceC2();
would work. In your case it is obvious to you that it will break, but the compiler can't prove that (easily). As such, your example is allowed, but will fail at runtime.
EDIT
The compiler looks at this:
ClassB clazzB1 = getClassA();
and sees that getClassA() returns a T that is defined as T extends ClassA, where ClassA is a class. It also sees that this T is assigned to ClassB, where ClassB is a class also. So it has to create a certain type that will conform to both ClassA and ClassB, something like a theoretical :
T extends ClassA & ClassB
but that is an impossible type, since no one can do ... extends ClassA, ClassB (this is simply not allowed in java). So the compiler fails. As a matter of fact if you do :
javac --debug=verboseResolution=all Test
you will see in the output, something like:
error: incompatible types: inference variable T has incompatible upper bounds ClassB,ClassA
On the other hand, when the compiler looks at this : ClassB clazzB3 = getInterfaceC2();, it sees that getInterfaceC2() returns:
T extends InterfaceC
and the result is assigned to ClassB, so it ends up with may be this type:
T extends ClassB & InterfaceC
which is perfectly valid. There can be such a type. I've shown you that you can create one as InterestingType, but as Holger pointed out, you could pass such a theoretical type yourself, via:
ClassB clazzB3 = Test.<InterestingType>getInterfaceC2();
this is called specifying a "type witness".