Back Contents Next

5. Inheritance

The statement that one entity inherits from another is a statement that the entity has all the same qualities as the the entity being inherited from. At least in any way observable from the outside.

To inherit from a type an object must first create an instance of that type, onto which it extends its own features. For this reason objects may only extend from types declared by a pattern declared to return self. In these cases there is a guaranteed way to get an instance of the super type.

5.1 Type Compatibility

Inheritance forms relationships between types. Remember that types are named through certain forms of patterns. If one type inherits from another then objects of that type are also of the first type. That is if a type A inherits from type B then all objects of type A are objects of type B. So when this specification refers to an expression yielding an object of a given type then it is implicit that the object may also be of a sub-type.

Inheritance creates type compatibility for objects. It does not do so for patterns. A pattern that inherits from a type is not compatible with the type or the super's pattern's type. An exception to this is if the pattern is declared to have the same parameters and a compatible return type.

The reason that the return type doesn't have to be the same is that two pattern types are equivalent under those circumstances. It can be seen that a pattern returning a lower type than expected can never cause a type problem. This is known as a covariant return type.

5.2 Super Type

The primary means of inheritance is to inherit from a single super type. Any entity may do this by declaring a super type. Objects that declare a super type inherit all the members of that super type. Patterns that declare a super type, incorporate that type into their type but more importantly they then generate objects which inherit from that type.

An object that is inherited from a super type contains all the entities that are members of the super type. In addition it may add its own members. Because of the way access control works the sub type will be unable to see some members of its super type. In these cases it may generally make members of the same name without interfering with its super type. An inherited object must first execute its super type's creation code before its own. When an object inherits from a type, its destruction block is run first when the object is destroyed. Then the super's destruction block is executed. This insures proper cleanup of all parts of the object.

Since an object must execute its super's creation code before its own. It may be achieved by a special syntax with the 'super' keyword (see 7.4.3 Super Keyword). The syntax is:

access-modifier name :super-type ()-> return-type
{
    super(param-value, ...);
    pattern-body
}

This allows the invocation of the super's creation code with whatever parameters are chosen. If the super's pattern takes no arguments then the super call statement may be omitted.

5.3 Virtual Patterns

Normally a named pattern of an anonymous type may not be assigned a different value. However a very similar effect does occur through inheritance. This is because all named patterns of an anonymous type are virtual. This means that in a sub-type a pattern of the same name with a compatible type may be declared which overrides the pattern in the super class. Then when the object is used the new version will be used whenever the pattern is invoked. This will occur even if the caller is unaware of the exact type of the object. This determination of the correct pattern at runtime is known as polymorphism. Polymorphism is one of the fundamental aspects of object oriented programming.

5.4 Abstract Patterns

The basic modifier 'abstract' can be used to indicate that a pattern can't be invoked, and that it must be overridden before the type can be complete. Any pattern containing an abstract pattern must be declared abstract. If a pattern inherits an abstract member without overriding it then the pattern contains that abstract member and is and must be declared abstract. An abstract pattern containing abstract patterns may only be invoked by a sub-type when all its abstract members have been overridden. A pattern can't be abstract if it contains no abstract patterns. access-modifier abstract name :super-type ()-> return-type
{
    pattern-body
}

Patterns may also be abstract by omitting the pattern body. In this case they clearly can't be invoked. To indicate that no pattern body is being declared a semicolon must be used to terminate the declaration, as:

access-modifier abstract name :super-type ()-> return-type;

Abstract is a pattern type modifier so that an abstract pattern may only be assigned to a pattern property of an abstract type. Normal patterns may assigned to abstract properties though.

5.5 Interfaces

An object's interface is the names and types of all its members. This determines how one interacts with the object. Notice that all objects of the same type have the same interface. When one refers to an object's interface it is usually intended to mean the public interface. The public interface is comprised of the names and types of all public members of the object. That doesn't include private and protected members. Members declared scope may or may not be included depending on context. What is important from an object's interface depends on the situation. Under different circumstances the object's public, scope, protected and private members are part of the interface. Notice that personal members are never part of the interface.

5.6 Implementing Interfaces

Even though an object may not be of a given type, it can be possible to masquerade as an object of that type if it can implement the interface of the object. For this to be possible the super type must have no private members or scope members that are not visible. If either of these were the case it wouldn't be possible to accurately fulfill the interface in all instances. Also, all these members must be patterns that may be overridden. This is because only these entities are virtual providing the necessary polymorphic behavior. In these situations a type's interface may be implemented by an entity by listing it after the second colon (see 2. Entities). Multiple interfaces may be implemented at the same time.

Implementing an interface is very similar to extending a type. The difference is that all the patterns in the interface are assumed to be abstract and without a pattern body. This means that an object implementing an interface must provide a pattern to override each pattern in the interface. It also may not invoke any method from the interface type. If a pattern is implementing an interface and does not override some pattern in the interface then it may be declared abstract and is treated exactly as if it were extending a type with an abstract pattern in it.



Back Contents Next

jwalker@cs.oberlin.edu