Original address: medium.com/golangspec/…

Key points:

  • An interface is a collection of methods
  • A single type can implement multiple methods
  • The same interface can be implemented by multiple types
  • The interface declaration can embed other interfaces, import all methods of the embedded interface (exportable methods + non-exportable methods), and import all layers of embedded interfaces into the interface declaration
  • Disallow loop embedding of interfaces
  • Method names within the interface must be unique: Custom methods and embedded interfaces contain methods with unique names
  • Interface variables can hold values of all types that implement the interface: abstract theoretical implementations
  • Static VS dynamic typing: Variables of an interface type can be assigned to each other by the types of the interface, dynamic typing
  • Interface type variables: dynamic type, dynamic value. A variable of this interface type is nil only if both are nil
  • Empty interface: Any type of variable can be hosted, or any type can implement (satisfy) an empty interface
  • Interface implementation: A type defines all methods that contain an interface declaration (method name + signature)
  • A value of an interface type can access only the methods defined by the interface itself, not other variables of the original type: behavior abstraction, associative comparison to polymorphisms in Java when subclasses are assigned to a parent class

Keywords: Interface, type, method, function, method signature, Exported method, satisfy, implement, etc.

Interface makes code more flexible and extensible, and is the realization of polymorphism in Golang. Interfaces allow you to specify that only certain behaviors are required, rather than specifying a specific type. A behavior is defined as a collection of methods, as shown in code snippet 1:

type I interface {
    f1(name string)
    f2(name string) (error, float32)
    f3() int64
}
Copy the code

No mandatory interface implementation is required. We say that a type implements or satisfies an interface, as long as it defines the method name and signature (input and return parameters) that the interface expects, as shown in section 2:

type T int64 func (T) f1(name string) { fmt.Println(name) } func (T) f2(name string) (error, float32) { return nil, 10.2} func (T) f3() int64 {return 10}Copy the code

In this example, type T satisfies interface I defined in code section 1, and the value of type T can be passed as an argument to any method that receives interface I. Code snippet 3:

type I interface { M() string } type T struct { name string } func (t T) M() string { return t.name } func Hello(i I) { Printf("Hi, my name is %s\n", i.M())} func main() {Hello(T{name: "Michał"}) // "Hi, my name is Michał"}Copy the code

In the Hello function, the method call i.M() is implemented in a specific way: different types of methods can be called when the method is an implementation of a type-satisfied interface. Golang’s important feature: The interface is implemented implicitly, and the programmer does not need to show that the declaration type T implements interface I. This is done automatically by the Go compiler (never ask a human to do what a machine should do). The elegant implementation of this behavior makes it possible to define an interface that is automatically implemented by already written types (with no modifications to previously completed types). Note: This language-level feature automatically implements the new interface if the existing type is not modified when the interface is added. This polymorphic approach has great flexibility.


This feature provides great flexibility for interfaces: a single type can implement multiple interfaces, sample code

type I1 interface { M1() } type I2 interface { M2() } type T struct{} func (T) M1() { fmt.Println("T.M1") } // Type T implements the interface I1 func (T) M2() {fmt.println (" t.m1 ")} // Type T implements the interface I2 func f1(ii1) {i.m1 ()} func f2(ii2) {i.m1 ()} func main() { t := T{} f1(t) // "T.M1" f2(t) // "T.M2" }Copy the code

Or the same interface can be implemented by multiple types, sample code

Type I interface {M()} type T1 struct{} func (T1) M() {ftt.println (" t1.m ")} M () {FMT. Println (" T2. M ")} / / type T2 implements the interface I func f (I) I {i.M ()} func main () {f (T1 {}) / / "T1. M" f (T2 {}) / / T2. The "M"}Copy the code

In addition to the methods required by one or more interfaces, types are free to implement different methods.

In Golang, there are two concepts about interfaces:

  1. Interface: The set of methods necessary to implement this Interface, by keywordinterfaceDefinition.
  2. Interface Type: The Interface type variable can hold the type value that implements any particular Interface.

    These two concepts are discussed separately below.

    Define an interface

    Interface definition: methods, embedded other interfaces

    The declaration of the interface specifies the methods that belong to the interface, and the method definition is done by its name and signature (input and return parameters), as follows:

    Type I interface {// Define an interface I that contains four methods m1() m2(int) m3(int) int m4() int}Copy the code

In addition to containing methods, embedded interfaces are also allowed – in this case, the interface imports all methods of the embedded interface into its own definition, as shown in the following example:

import "fmt"
type I interface {
     m1()
}
type J interface {
    m2()
    I
    fmt.Stringer
}
Copy the code

Interface J contains the following method set:

Inline interface When multiple levels of inline, import all methods that contain the interface

If interface I is embedded with interface J, and interface K is embedded with other interface K, all methods of interface K will also be added to the declaration of interface I, as follows:

type I interface {
    J
    i()
}
type J interface {
    K
    j()
}
type K interface {
    k()
}
Copy the code

Then interface I contains methods: I (), j(), k()

Disallow loop embedding of interfaces

Circular embedding of interfaces is disallowed and will be detected while compilation (source code): Error “interface type loop involving” is forbidden, and errors are detected during compilation.

type I interface {
    J
    i()
}
type J interface {
    K
    j()
}
type K interface {
    k()
    I
}
Copy the code

The method name within the interface must be unique

Duplicate method I: duplicate method id: duplicate method id: duplicate method id: duplicate method id

type I interface {
    J
    i()
}
type J interface {
    j()
    i(int)
}
Copy the code

This form of interface assembly runs through various library definitions, an example of IO.ReaderWriter:

type ReadWriter interface {
    Reader
    Writer
}
Copy the code

Now that we know how to create an interface, let’s look at values of interface types.

Variable of the interface type

A variable of type interface I can hold any value that implements interface I, as shown in the following example:

Struct {} func (T) method1() {} func main() {var I I = T{} If T implements interface I, I can store the value of a variable of type T fmt.println (I)}.Copy the code

Static versus dynamic typing

The existing type of a variable is specified at compile time, specified at declaration time, and never changed. This situation is called static type or simply type. Variables of interface type also have a static type, the interface itself; In addition, they have dynamic types, which are assignable types. Sample code:

Type I struct {} func (T1) M() {} type I struct {} func (T2) M() { Func main() {var I I = T1{} Printf("%T\n", I) // output main.T1 I = T2{} fmt.Printf("%T\n", I) // Output main.T2 _ = I}Copy the code

The static type of variable I is interface I, and that doesn’t change. The dynamic type, on the other hand, is dynamically variable. After the first assignment, the dynamic type of I is of type T1, but this is not fixed. The second assignment changes the dynamic type of I to type T2. Dynamic type is not set when the variable value of the interface type is nil (zero value of the interface is nil).

How do I get the dynamic type of an interface type variable

  1. The Reflect package provides methods to get dynamic types. Example code: The Reflect package will report runtime error when the variable is nil.

    fmt.Println(reflect.TypeOf(i).PkgPath(), reflect.TypeOf(i).Name())
    fmt.Println(reflect.TypeOf(i).String())
    Copy the code
  2. The FMT package can also get the dynamic type of a variable by formatting the verb %T: while FMT is also implemented using Reflect, it can also be supported when the variable I is nil.

    fmt.Printf("%T\n", i)
    Copy the code

Interface type nil

Take a look at the following code example:

Type I interface {M()} type T struct {} func (T) M() {} func main() {var T *T // T must be nil if T == nil Else {fmt.Println("t is not nil")} var I I = t // t is null, but I? if i == nil { fmt.Println("i is nil") } else { fmt.Println("i is not nil") } }Copy the code

Output result:

t is nil
i is not nil
Copy the code

The value assigned to variable I is nil, but I is not nil. The interface type variable contains two parts:

Printout:

F() = <nil>
F() is nil: false
type of F(): *main.T
Copy the code

Just because interface type value returned from function has dynamic value set (* main.t), it isn’t equal to nil.

Because the function returns an interface value of dynamic type *main.T, it does not equal nil

The empty interface

The method set of the interface can be completely empty.

type I interface {}
type T struct {}
func (T) M() {}
func main() {
    var i I = T{}
    _ = i
}
Copy the code

Empty interfaces are automatically implemented by any type, so any type can be assigned to variables of this empty interface type. A dynamic or static type of an empty interface behaves the same as a non-empty interface. FMT.Println function variable parameter hollow interface is widely used. TODO: How do you implement mutable parameters?

Implementing an interface

Implementation methods Any type of all methods automatically satisfies (implements) this interface. You don’t need to show which interface a declaration type implements, as you do in Java. The Go compiler automatically detects the implementation of the type-pair interface, which is a powerful feature of Golang at the language level. Sample code:

Import (" FMT ""regexp") type I interface {Find(b []byte) []byte // interface I contains method Find, and regexp implementation contains method implementation (method name + signature), Func f(I I) {FMT.Printf("%s\n", Int ([]byte(" ABC ")))} func main() {var re = regexp.MustCompile(' b ')}Copy the code

Here we define an interface I, without modifying the built-in regexp module, such that the: regexp.Regexp type implements interface I.

  • A type can implement multiple interfaces, and an interface can be implemented by multiple types
  • An interface that implements an interface and assigns a value to the interface type can access only the methods defined by the interface itself

Abstraction of interface type behavior

Interface type values can only access methods of that interface type, which hide other values contained in the original type, such as structs, arrays, Scalar, and so on. Sample code:

type I interface { M1() } type T int64 func (T) M1() {} func (T) M2() {} func main() { var i I = T(10) // M1() method i.m1 () i.m2 () // i.m2 undefined (type I has no field or method M2)}Copy the code

Read more

  • The Go Programming Language Specification – The Go Programming Language
  • research! rsc: Go Data Structures: Interfaces
  • How to use interfaces in Go