10. Interfaces

An interface declaration declares an interface type, i.e., a reference type that:

  • Includes constants, properties, and methods as its members;

  • Has no instance variables;

  • Usually declares one or more methods;

  • Allows otherwise unrelated classes to provide implementations for the methods, and so implement the interface.

Interfaces cannot be instantiated directly.

Interfaces can be top-level and local (see Local Classes and Interfaces).

An interface can be declared a direct extension of one or more other interfaces. In that case the interface inherits all members from the interfaces it extends (except any members it overrides or hides).

A class can be declared to directly implement one or more interfaces. Any instance of the class implements all methods specified by the interface(s). A class implements all interfaces that its direct superclasses and direct superinterfaces implement. The interface inheritance allows objects to support common behaviors without sharing a superclass.

The value of a variable declared as an interface type can be a reference to any instance of the class that implements the specified interface. However, it is not enough for the class to implement all methods of the interface. The class or one of its superclasses must be actually declared to implement the interface. Otherwise such class is not considered to implement the interface.

Interfaces without interfaceExtendsClause have class Object as their supertype (see Object Class Type). It enables assignments on the basis of reference types conversions (see Widening Reference Conversions).


10.1. Interface Declarations

An interface declaration specifies a new named reference type:

interfaceDeclaration:
    'interface' identifier typeParameters?
    interfaceExtendsClause? '{' interfaceMember* '}'
    ;

interfaceExtendsClause:
    'extends' interfaceTypeList
    ;

interfaceTypeList:
    typeReference (',' typeReference)*
    ;

The identifier in an interface declaration specifies the interface name.

An interface declaration with typeParameters introduces a new generic interface in Generic Declarations.

The scope of an interface declaration is defined in Scopes.

The interface declaration shadowing is specified in Shadowing Parameters.


10.2. Superinterfaces and Subinterfaces

An interface declared with an extends clause extends all other named interfaces, and thus inherits all their members. Such other named interfaces are direct superinterfaces of a declared interface.

A class that implements the declared interface also implements all the interfaces that the interface extends.

A compile-time error occurs if:

  • typeReference in the extends clause of an interface declaration names an interface type that is not accessible (see Scopes).

  • Type arguments of typeReference denote a parameterized type that is not well-formed (see Generic Instantiations).

  • There is a cycle in extends graph.

  • At least one of typeReference’s is an alias of one of primitive or enum types.

  • Any type argument is a wildcard type.

Each typeReference in the extends clause of an interface declaration must name an accessible interface type (see Scopes). Otherwise, a compile-time error occurs.

If an interface declaration (possibly generic) I <F1,…, Fn> (\(n\geq{}0\)) contains an extends clause, then the direct superinterfaces of the interface type I <F1,…, Fn> are the types given in the extends clause of the declaration of I.

The direct superinterfaces of the parameterized interface type I <T1,…, Tn> are all types J <U1\(\theta{}\),…, Uk\(\theta{}\)>, if:

  • Ti (\(1\leq{}i\leq{}n\)) is the type of a generic interface declaration I <F1,…, Fn> (\(n > 0\));

  • J <U1,…, Uk> is a direct superinterface of I <F1,…, Fn>; and

  • \(\theta{}\) is the substitution [F1 := T1,…, Fn := Tn].

The transitive closure of the direct superinterface relationship results in the superinterface relationship.

Wherever K is a superinterface of the interface I, I is a subinterface of K.

The interface K is a superinterface of the interface I if:

  • I is a direct subinterface of K; or

  • K is a superinterface of some interface J of which I is, in its turn, a subinterface.

There is no single interface to which all interfaces are extensions (unlike class Object to which every class is an extension).

If the extends clause of I mentions T as a superinterface, or as a qualifier in the fully qualified form of a superinterface name, then the interface I directly depends on type T.

Moreover, the interface I depends on a reference type T if:

  • I directly depends on T; or

  • I directly depends on the class C that depends on T (see Classes); or

  • I directly depends on the interface J that, in its turn, depends on T.

A compile-time error occurs if an interface depends on itself.

ClassCircularityError is thrown if circularly declared interfaces are detected as interfaces and loaded at runtime.


10.3. Interface Body

The body of an interface may declare members of the interface, i.e., properties (see Interface Declarations) and methods (see Method Declarations).

interfaceMember
    : interfaceProperty
    | interfaceMethodDeclaration
    ;

The scope of declaration of a member m that the interface type I declares or inherits is specified in Scopes.


10.4. Interface Members

Interface type members are as follows:

An interface without a direct superinterface implicitly declares the following:

  • Abstract-member method m (see Interface Method Declarations) with signature s;

  • Return type r and ‘throws’ clause t that correspond to each public instance method m with signature s;

  • Return type r and ‘throws’ clause t declared in Object (see Object Class Type);

—if the interface does not explicitly declare an abstract method (see Interface Method Declarations) with the same signature and return type, and a compatible ‘throws’ clause.

A compile-time error occurs if the interface explicitly declares:

  • Method m that Object declares as final.

  • A method with a signature that is override-equivalent (see Signatures) to the Object’s public method, but is not abstract, and has a different return type or an incompatible ‘throws’ clause.

An interface normally inherits all members of the interfaces it extends. However, an interface does not inherit the following:

A name in a declaration scope must be unique, i.e., the names of fields and methods of an interface type must not be the same (see Interface Declarations).


10.5. Interface Properties

An interface property can be defined in the form of a field or an accessor (a getter or a setter):

interfaceProperty:
    readonly? identifier '?'? ':' type
    | 'get' identifier '(' ')' returnType
    | 'set' identifier '(' parameter ')'
    ;

If a property is defined in the form of a field, then it implicitly defines the following:

  • A getter, if a field is marked as readonly;

  • Otherwise, both a getter and a setter with the same name.

If ‘?’ is used after the name of the property, then its actual type is type | undefined.

As a result, the effect of the following definitions is the same:

1 interface Style {
2     color: string
3 }
4 // is the same as
5 interface Style {
6     get color(): string
7     set color(s: string)
8 }

A class that implements an interface with properties can also use a field or an accessor notation (see Implementing Interface Properties).


10.6. Interface Method Declarations

An ordinary interface method declaration that specifies the method’s name and signature is called abstract.

As experimental features, an interface method can have a body (see Default Method Declarations) and be static (see Static Method Declarations).

interfaceMethodDeclaration:
    interfaceMethodOverloadSignature*
    identifier signature
    | interfaceDefaultMethodDeclaration
    | interfaceStaticMethodDeclaration
    ;

The methods declared within interface bodies are implicitly public.

A compile-time error occurs if the body of an interface declares:

  • A method with a name already used for a field in this declaration.

  • Two methods (overridden explicitly or implicitly) with override-equivalent signatures (see Signatures), if such signatures are not inherited (see Inheritance and Overriding).


10.6.1. Interface Method Overload Signatures

ArkTS allows specifying a method that can have several overload signatures but a single name.

interfaceMethodOverloadSignature:
    identifier signature
    ;

Calling a method with overload signatures means that that the method called implements the overload signature that is textually the last.

The Overload signature compatibility requirements are discussed in Overload Signature Compatibility.

In the example below, one overload signature is parameterless, and the other two have one parameter each:

 1 interface C {
 2     foo(): void           // 1st signature
 3     foo(x: string): void  // 2nd signature
 4     foo(x?: string): void // 3rd - implementation signature
 5 }
 6 function demo (c: C) {
 7    c.foo()           // ok, call fits 1st and 3rd signatures
 8    c.foo("aa")       // ok, call fits 2nd and 3rd signatures
 9    c.foo(undefined)  // ok, call fits the 3rd signature
10 }

If a class implements an interface that has a method with an overload signature, then the class must also provide a method that has an overload signature.

 1 class Base { ... }
 2 class Derived extends Base { ... }
 3
 4 interface Interface {
 5   foo (p: Derived)
 6   foo (p: Base)
 7 }
 8
 9 class Class implements Interface {
10   foo (p: Derived)
11   foo (p: Base) { ... }
12 }

10.6.2. Inheritance and Overriding

The interface I inherits any abstract and default method m from its direct superinterfaces if all of the following is true:

  • m is a member of I’s direct superinterface J;

  • I declares no method with a signature that is compatible with the signature of m (see Compatible Signature);

  • No method \(m'\) that is a member of I’s direct superinterface \(J'\) (where m is distinct from \(m'\), and J from \(J'\)) overrides the declaration of the method m from \(J'\).

An interface does not inherit private or static methods from its superinterfaces.

A compile-time error occurs if:

  • The interface I declares a private or static method m;

  • The signature of m is compatible with the public instance method \(m'\) in a superinterface of I (see Compatible Signature); and

  • \(m'\) is otherwise accessible to code in I.


10.6.3. Overriding by Instance Methods

The instance method mI (declared in, or inherited by the interface I) overrides another instance method mJ of I (declared in interface J) if all of the following is true:

  • J is a superinterface of I;

  • I does not inherit mJ;

  • The signature of mI is compatible with (see Compatible Signature) the signature of mJ; and

  • mJ is public.


10.6.4. Overriding Requirements

The following kinds of relationships are described in Requirements in Overriding and Hiding:

  • The relationship between the return type of an interface and that of any overridden interface method.

  • The relationship between the ‘throws’ clause of an interface method and that of any overridden interface method.

  • The relationship between the signatures of an interface method and that of any overridden interface method.

  • The relationship between the accessibility of an interface method and that of any overridden interface method.

A compile-time error occurs if a default method is override-equivalent to a non-private method of the class Object. Any class that implements interface must inherit the method’s own implementation.


10.6.5. Interfaces Inheriting Methods with Override-Equivalent Signatures

An interface can inherit several methods with override-equivalent signatures (see Override-Equivalent Signatures).

A compile-time error occurs if the interface I inherits a default method with a signature that is override-equivalent to an abstract or default method inherited by I.

However, the interface I inherits all methods that are abstract.

A compile-time error occurs if one of the inherited methods for every other inherited method is not return-type-substitutable. A ‘throws’ clause causes no error in such cases.

The same method declaration can use multiple paths of inheritance from an interface. It causes no compile-time error on its own.