You encountered a known sloppiness of HotSpot’s Verifier, not to verify the type compatibility of receiver types for invokeinterface
calls. But this does not imply that such code is formally correct.
JVMSpec, §4.9.2 Structural Constraints states:
- The type of every class instance that is the target of a method invocation instruction must be assignment compatible with the class or interface type specified in the instruction (JLS §5.2).
However, there was a practical problem with verifying this constraint statically for verifiers of older JVMs following the algorithm which is now called “Verification by type inference” and still is the standard for class files having a version below 50. This answer explains the issue. If two different reference types have to be merged after branches, it may result in a common super type not implementing the interface while both types actually do. So rejecting a subsequent invokeinterface
call could lead in rejecting correct code.
Starting with version 50, there is “Verification by type checking”, which is even mandatory for versions higher than 50. It uses stackmap tables, explicitly declaring the supposed result of type merging, eliminating the expensive operations. Consequently, the “Verification by type checking” has formal rules which mandate that the static type is compatible with the interface type:
invokeinterface
An invokeinterface instruction is type safe iff all of the following conditions hold:
…
- One can validly replace types matching the type
MethodIntfName
and the argument types given in Descriptor
on the incoming operand stack with the return type given in Descriptor
, yielding the outgoing type state.
(Note that these rules are identical to the rules of an invokevirtual
instruction, except that it uses MethodIntfName
instead of MethodClassName
)
As a side note, the code would be correct in an environment where java/lang/Object
happens to implement both, org/mydomain/Foo
and org/mydomain/Bar
. It’s only that verification against the actual environment that is missing here.
To put it straight, your code happens to work on HotSpot due to a missing check, but is not portable, i.e. may fail on other JVMs and may even fail on future versions of HotSpot, where the type checking rules are enforced.
There is no performance advantage in omitting the checkcast
, as there will be a check one way or the other and a throwable will be thrown if the type doesn’t match. Therefore, I would stick to creating formally correct code, even if this particular JVM does not enforce it.