Interface Extension
For example, in the following case, the type Array does not inherently implement the interface PrintSizeable. However, we can use extension to add an additional member function printSize to Array and implement PrintSizeable.
After extending Array to implement PrintSizeable, it is equivalent to Array having implemented PrintSizeable at its definition time.
Therefore, Array can be used as an implementation type of PrintSizeable, as shown in the following code.
Compiling and executing the above code yields the output:
Multiple interfaces can be implemented simultaneously within the same extension. Separate the interfaces with &, and their order does not matter.
As shown in the following code, we can implement I1, I2, and I3 for Foo in a single extension.
Additional generic constraints can also be declared in interface extensions to satisfy interfaces under specific conditions.
For example, we can make the Pair type implement the Eq interface, allowing Pair itself to become a type that satisfies the Eq constraint, as shown below.
Compiling and executing the above code yields the output:
If the extended type already includes the functions or properties required by the interface, these functions or properties need not (and cannot) be re-implemented in the extension.
For example, in the following case, a new interface Sizeable is defined to retrieve the size of a type. Since Array already contains this function, we can extend Array to implement Sizeable without adding additional functions.
Compiling and executing the above code yields the output:
When interface extensions implement interfaces with inheritance relationships, the extensions are checked in the order of "first checking extensions implementing parent interfaces, then checking extensions implementing child interfaces."
For example, if interface I1 has a child interface I2, and I1 contains a default implementation, while type A has two extensions implementing the parent and child interfaces respectively, the extension implementing I1 will be checked first, followed by the extension implementing I2.
Compiling and executing the above code yields the output:
In this example, when checking the extension implementing I1, the foo function is inherited from I1. When checking the extension implementing I2, since A already has an inherited default implementation of foo with the same signature, this foo will be overridden. Thus, when calling A's foo function, it ultimately points to the implementation in I2 (the child interface).
If two interface extensions of the same type implement interfaces with conflicting inheritance relationships, making it impossible to determine the checking order, an error will be reported.
If two interface extensions of the same type implement interfaces without inheritance relationships, they will be checked simultaneously.
> Note:
>
> When class A has a generic base class B, and B extends an interface I with default implementations of instance or static functions (e.g., foo), if this function is not overridden in B or its extensions, and class A does not directly implement the interface I, calling the function foo through an instance of class A may lead to unexpected behavior.
interface PrintSizeable {
func printSize(): Unit
}
extend<T> Array<T> <: PrintSizeable {
public func printSize() {
println("The size is ${this.size}")
}
}main() {
let a: PrintSizeable = Array<Int64>()
a.printSize() // 0
}The size is 0interface I1 {
func f1(): Unit
}
interface I2 {
func f2(): Unit
}
interface I3 {
func f3(): Unit
}
class Foo {}
extend Foo <: I1 & I2 & I3 {
public func f1(): Unit {}
public func f2(): Unit {}
public func f3(): Unit {}
}class Pair<T1, T2> {
var first: T1
var second: T2
public init(a: T1, b: T2) {
first = a
second = b
}
}
interface Eq<T> {
func equals(other: T): Bool
}
extend<T1, T2> Pair<T1, T2> <: Eq<Pair<T1, T2>> where T1 <: Eq<T1>, T2 <: Eq<T2> {
public func equals(other: Pair<T1, T2>) {
first.equals(other.first) && second.equals(other.second)
}
}
class Foo <: Eq<Foo> {
public func equals(other: Foo): Bool {
true
}
}
main() {
let a = Pair(Foo(), Foo())
let b = Pair(Foo(), Foo())
println(a.equals(b)) // true
}trueinterface Sizeable {
prop size: Int64
}
extend<T> Array<T> <: Sizeable {}
main() {
let a: Sizeable = Array<Int64>()
println(a.size)
}0interface I1 {
func foo(): Unit { println("I1 foo") }
}
interface I2 <: I1 {
func foo(): Unit { println("I2 foo") }
}
class A {}
extend A <: I1 {} // first check
extend A <: I2 {} // second check
main() {
A().foo()
}I2 foointerface I1 {}
interface I2 <: I1 {}
interface I3 {}
interface I4 <: I3 {}
class A {}
extend A <: I1 & I4 {} // error: unable to decide which extension happens first
extend A <: I2 & I3 {} // error: unable to decide which extension happens firstinterface I1 {
func foo() {}
}
interface I2 {
func foo() {}
}
class A {}
extend A <: I1 {} // Error, multiple default implementations, need to re-implement 'foo' in 'A'
extend A <: I2 {} // Error, multiple default implementations, need to re-implement 'foo' in 'A'interface I<N> {
func foo(n: N): N {n}
}
open class B<T> {}
extend<T> B<T> <: I<T> {}
class A <: B<Int64>{}
main() {
A().foo(0) // this call triggers unexpected behaviour
}