Figure 12 presents an example of a class/subclass pair
in which a method (in this case ) of a class type checks
fine under the assumption that MyType is exactly the type generated by
the class (in this case
), but when inherited causes a
run-time error due to a typing problem.
The subclass simply adds a new instance variable and method, and redefines the
method,
(without changing the typing). If
is
an object generated by class
and
is an element
generated by
(and hence having type
), then
the call
will cause a run-time error while within the body of
the call of
made from the body of
. The
problem is that the receiver,
, is of type
,while the argument is of type
. Hence, when the version
of
from
is called, it will send the message
to the parameter,
, which does not have a
corresponding method to handle the call, causing a run-time error.
Notice that the problem arises in the method which was not
changed in
.According to our type-checking rules, the type of
should
be Func(SubTestType): Bool (obtained from
).
As a result, the application to a parameter of type
is
clearly an error.
How would the weaker type-checking assumption adopted in this section keep
us from this error?
If we type check the methods of under the assumption that
, then the body of the method
will not type check. The problem is that the message send
in the body of
requires a parameter of type MyType,
while
has type
. Because we only know that
, we cannot deduce that
also has
type MyType. Thus type checking the method body of
results
in a type error, keeping us out of the trouble illustrated above.
This subtleness of type checking may make the reader nervous about uses of MyType, but the arguments presented earlier in this section should be convincing for its safety. Moreover, formal proofs of the soundness of the type-checking rules exist, and can be found in [Bru94] and [BSvG95], for example.
The
appendix includes an example
of a method that type checks under the strong assumption that
MyType = , but that fails under the weaker assumption that
. As a result, ignoring the fact that the meaning of
MyType changes in subclasses can result in broken code. Type-checking under
the matching assumption guarantees that no type errors result during execution.