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:

RadioClock inherits from Radio and AlarmClock, while AlarmClock inherits from Clock.

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:

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

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:

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.

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!

It is understood that all interfaces inherit from type Object; you are not allowed to restate that.

Type Object is mapped to an abstract type by the various language mappings, so you cannot instantiate an Ice object of that type.