Design‎ > ‎Classes and Interfaces‎ > ‎

Reference Type

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.


Comments