Interface Inheritance

5 min read

5 min read

5 min read

5 min read

5 min read

5 min read

5 min read

5 min read

5 min read

Interfaces support inheritance. For example, we could extend our Clock interface to support the concept of an alarm clock:

Slice
module M
{
interface AlarmClock extends Clock
{
idempotent TimeOfDay getAlarmTime();
idempotent void setAlarmTime(TimeOfDay alarmTime)
throws BadTimeValException;
}
}

AlarmClock inherits all the operations of Clock: an AlarmClock object provides the getTime and setTime operations in addition to its own getAlarmTime and setAlarmTime operations.

Multiple interface inheritance is also possible. For example, we can construct a radio alarm clock as follows:

Slice
module M
{
interface Radio
{
void setFrequency(long hertz) throws GenericException;
void setVolume(long dB) throws GenericException;
}
enum AlarmMode { RadioAlarm, BeepAlarm }
interface RadioClock extends Radio, AlarmClock
{
void setMode(AlarmMode mode);
AlarmMode getMode();
}
}

RadioClock extends both Radio and AlarmClock and can therefore be passed where a Radio, an AlarmClock, or a Clock is expected. The inheritance diagram for this definition looks as follows:

Radio clock interface inheritance RadioClock inherits from both Radio and AlarmClock. AlarmClock in turn inherits from Clock. Hollow triangular arrowheads point from each derived interface toward its base interface. INTERFACE Clock INTERFACE Radio INTERFACE AlarmClock INTERFACE RadioClock

Radio clock interface inheritance.

Interfaces that inherit from more than one base interface may share a common base interface. For example, the following definition is legal:

Slice
interface B { /* ... */ }
interface I1 extends B { /* ... */ }
interface I2 extends B { /* ... */ }
interface D extends I1, I2 { /* ... */ }

This definition results in the familiar diamond shape:

Diamond interface inheritance D inherits from I1 and I2, which both inherit from B. Hollow arrowheads point toward each base interface. INTERFACE B INTERFACE I1 INTERFACE I2 INTERFACE D

If an interface uses multiple inheritance, it must not inherit the same operation name from more than one base interface. For example, the following definition is illegal:

Slice
interface Clock
{
void set(TimeOfDay time); // set time
}
interface Radio
{
void set(long hertz); // set frequency
}
interface RadioClock extends Radio, Clock // Illegal!
{
// ...
}

This definition is illegal because RadioClock inherits two set operations, Radio::set and Clock::set. The Slice compiler makes this illegal because (unlike C++) many programming languages do not have a built-in facility for disambiguating the different operations. In Slice, the simple rule is that all inherited operations must have unique names. (In practice, this is rarely a problem because inheritance is rarely added to an interface hierarchy "after the fact". To avoid accidental clashes, we suggest that you use descriptive operation names, such as setTime and setFrequency. This makes accidental name clashes less likely.)

All Slice interfaces are ultimately derived from Object. For example, the radio clock inheritance hierarchy shown earlier would be shown more correctly as:

Implicit inheritance from Object Radio and Clock implicitly inherit from Object. AlarmClock inherits from Clock, and RadioClock inherits from Radio and AlarmClock. Hollow arrowheads point toward the base interfaces. INTERFACE Object INTERFACE Clock INTERFACE Radio INTERFACE AlarmClock INTERFACE RadioClock Implicit inheritance Implicit inheritance

Because all interfaces have a common base interface, we can pass any type of interface as that type. For example:

Slice
interface ProxyStore
{
idempotent void putProxy(string name, Object* o);
idempotent Object* getProxy(string name);
}

Object is a Slice keyword (note the capitalization) that denotes the root type of the inheritance hierarchy. The ProxyStore interface is a generic proxy storage facility: the client can call putProxy to add a proxy of any type under a given name and later retrieve that proxy again by calling getProxy and supplying that name. The ability to generically store proxies in this fashion allows us to build general-purpose facilities, such as a naming service that can store proxies and deliver them to clients. Such a service, in turn, allows us to avoid hard-coding proxy details into clients and servers.

Inheritance from type Object is always implicit. For example, the following Slice definition is illegal:

Slice
interface MyInterface extends Object { /* ... */ } // Error!

Every interface inherits the four operations of Object: ice_ping, ice_isA, ice_id, and ice_ids.