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.