When a method has an argument that is an object, and a method is invoked on this object, one wants to know what method exactly is being used. In some languages it is allowed to use the type of a super class, but actually pass an object of a subclass. It may look like this: CLASS Super PROC echo() IO.write("Super") } } CLASS Child EXTENDS Super PROC echo() IO.write("Child") } } My.do(Super.NEW()) My.do(Child.NEW()) # In another file MODULE My PROC do(Super s) s.echo() # results in "Super" or "Child" } } The problem here is that in the My.do() method we have no clue that the subclass exists. We assume that "s" is an instance of Super. But it may in fact be a subclass of Super. This makes behavior unpredictable. Alternatives: 1. Add @final to a class to signal that no subclasses exist. This helps when there are no subclasses, but doesn't actually help for the example. 2. Add PARENT to signal that subclasses may exist. Better, but one has to go to the class to find out. And it's still not possible to have an object that can only be an instantiation of the parent class. 3. Use @default on methods that can be overruled in a subclass and @replace on a method in a subclass that overrules a method in the parent. This is useful, but still requires a look in the class definition. 4. Add something to the method declaration to signal that the object may in fact be a subclass of this class. For example: MODULE My PROC do(SOME Super s) s.echo() # results in "Super" or "Child" } } o Works well, but looks strange. - Adds a new mechanism 5. Use an interface: INTERFACE I_Super PROC echo() } CLASS Super IMPLEMENTS I_Super PROC echo() IO.write("Super") } } CLASS Child EXTENDS Super # automatically implements I_Super PROC echo() IO.write("Child") } } My.do(Super.NEW()) My.do(Child.NEW()) # In another file MODULE My PROC do(I_Super s) # "I_" makes clear what type "s" is s.echo() # results in "Super" or "Child" } } + Looks good, uses existing interface mechanism - requires defining an interface for every parent class 6. Bright Idea: Every class implicitly defines an interface. Let's make a difference between the class itself and the interface it defines: For every class Aa automatically define an interface Aa.I. CLASS Super # automatically defines Super.I ... } CLASS Child EXTENDS Super # automatically implements Super.I ... } My.do(Super.NEW()) My.do(Child.NEW()) # Another file MODULE My PROC do(Super.I s) # "s" is an object with the interface of Super s.echo() # results in "Super" or "Child" } } + When reading the code you know exactly what it means. + When using "Super s" we know "s" is an instance of Super, not a subclass. + No need to define interfaces. Choice: 6 Conderation: When catching exceptions this looks a bit ugly: CATCH E.NilReference e # caught a NIL reference error CATCH E.Error.I e # caught any other kind of error And when E.NilReference gets sub-classed into a more detailed type it will no longer be caught. This might be nicer: CATCH E.NilReference e # caught a NIL reference error CATCH E.Error e # caught any other kind of error Then we need something else to refer to the specific class, not its children. We could use ".O" for this. |
Design > Classes and Interfaces >