This is the 24th day of my participation in the August Text Challenge.More challenges in August

The content of this article comes from my study of “Go language minimalist a General” and “Go Web programming practical school” and their own practice summary

define

Interface types are generalizations and abstractions of the behavior of other types. Interfaces are one of the most important features of the Go language. An interface type defines a set of methods, but does not contain concrete implementations of those methods

An interface is essentially a type, specifically a pointer type. The interface can realize multiple functions. If a type implements an interface, values of that type are supported everywhere the interface is used. The interface definition format is as follows:

typeThe name of the interfaceinterfaceMethod (parameter list) Return value list method2(parameter list) Return value list... }Copy the code

If an interface has no method declaration, it is an empty interface (interface{}). Its use is similar to that of object oriented and can be assigned to objects of any type. Interface variables default to nil. If the implementation interface type supports the equality operation, the equality operation can be performed. Otherwise, an error is reported. Example code:

package main

import "fmt"

func main(a) {
    var var1, var2 interface{}
    fmt.Println(var1 == nil, var1 == var2) // true true

    var1, var2 = 66.88
    fmt.Println(var1 == var2) // false

    var1, var2 = map[string]int{}, map[string]int{}
    fmt.Println(var1 == var2) / / an error
}
Copy the code

The assignment

Interfaces in Go do not support direct instantiation, but do support assignment operations to quickly map interfaces to implementation classes. There are two situations in Go:

  • Assigns an object instance that implements the interface to the interface
  • Assigns an interface to another interface

Assigns an object instance that implements the interface to the interface

To assign an instance of an object of a specified type to an interface, specify that the corresponding class implements all the methods required by the interface, otherwise it does not count as an implementation of the interface. For example, define a Num type and its associated methods:

Package main func main() {} type Num int func (x Num) Equal(I Num) bool {return x == I} // less than func (x Num) LessThan(I Num) bool {return x < I} // func (x Num) MoreThan(I Num) bool {return x > I} // func (x Num) Multiple(I Num) {*x = *x * I} func (x Num) Divide(I Num) {*x = *x/I}Copy the code

An interface NumI is then defined so that its Num type implements the NumI interface

type NumI interface {
    Equal(i Num) bool
    LessThan(i Num) bool
    MoreThan(i Num) bool
    Multiple(i Num)
    Divide(i Num)
}
Copy the code

Assigns an interface to the interface

In Go, as long as two interfaces have the same list of methods (regardless of order), they are equivalent and can be assigned to each other. For example, the following two packages:

package oop1

type NumInterface1 interface {
  Equal(i int) bool
  LassThan(i int) bool
  BiggerThan(i int) bool
}
Copy the code
package oop2

type NumInterface1 interface {
  Equal(i int) bool
  LassThan(i int) bool
  BiggerThan(i int) bool
}
Copy the code

In Go, there is no difference between the two interfaces because the NumInterface1 interface of the oOP1 package also implements all the methods of the NumInterface2 interface of the oop2 package. Any interface that implements either of the two interfaces, Then you can assign to another interface

The query

Interface queries are made while the program is running. The success of the query can also be determined at run time. Unlike the interface assignment compiler, which only needs static type checking to determine whether type assignment is feasible. In the Go language, you can ask if the object it points to is of a type, for example:

var filewriter Writer = ...
if file, ok := filewriter.(*File); ok {
  // ...
}
Copy the code

The if statement in the above code is used to determine whether the instance that the FileWriter interface only wants to object to is of type *File and executes specific code if so. The following is an example of interface query:

slice := make([]int.0)
slice = append(slice, 6.7.8)

var I interface{} = slice

if res, ok := I.([]int); ok {
  fmt.Println(res) / / [6, 7, 8]
  fmt.Println(ok)
}
Copy the code

The if statement in the code above determines whether the object to which interface I points is of type []int and, if so, enters the element in the slice.

combination

In Go, not only structures can be nested among structures, but interfaces can also be nested to create new interfaces. An interface can contain one or more other interfaces, which is equivalent to listing the methods of these embedded interfaces directly in the outer interface. If all methods of an interface are implemented, all methods of nested interfaces in that interface can be called

Interface combination is very simple, directly write the interface name inside the interface; In addition, you can redefine your own interface methods within the interface

1 / / interface
type Interface1 interface {
  Write(p []byte) (n int, err error)
}

2 / / interface
type Interfac2 interface {
  Close() error
}

// Interface combination
type IncerfaceCombine interface {
  Interface1
  Interface2
}
Copy the code

The above code defines three interfaces, among which the InterfaceCombine interface is a composite interface that has the features of both Interface1 and Interface2 interfaces

value

A value of an interface type (called an interface value) has two parts:

  • Concrete type (dynamic type)
  • Type value (dynamic value)

In Go, a type is only a compile-time concept, so it is not a value. The type descriptor can provide specific information about each type, such as its name and method. For an interface value, the type part is usually represented by the corresponding type descriptor

If the zero value of the interface value is nil, its dynamic value is nil. Whether an interface value is nil depends on its dynamic type. We can use xx! = nil or xx == nil, if you call a method on nil, it will crash your program

Common application

The error interface

In Go, the error interface is defined as follows:

type error interface {
	Error() string
}

type errorString struct {
	s string
}

func New(text string) error {
	return &errorString{text}
}

func (e *errorString) Error(a) string {
	return e.s
}
Copy the code

In the code above, we define an errorString structure with only one S member and type string inside

The Error method is implemented by the *errorString pointer, not the type errorString. Its purpose is to assign unequal error instance variables when calling New

Type inference

Type inference is written as follows:

i.(Type)
Copy the code
  • iIs an expression of interface type
  • TypeIt’s a type

Type inference checks whether the dynamic Type of I satisfies the specified Type. If I is null, type inference fails

When Type is a concrete Type, it is inferred whether the dynamic Type of I is Type; If the check succeeds, the dynamic Type that asserts I is Type; If the check fails, the program crashes

If Type is the Type of an interface, then the dynamic Type of I is inferred to be Type. If the check succeeds, the dynamic value is not successfully obtained and the result is an interface value. The interface value and value Type do not change, except that the resulting Type is interface Type Type

Type inference can be used to restore an interface variable to its original type or to determine whether a more specific interface type has been implemented. You can also use switch-case(also known as type branching) statements to make inferential matches between multiple types, so that empty interfaces have more room to play with

package main

import "fmt"

func main(a) {
	var a interface{} = func(a int) string {
		return fmt.Sprintf("d:%d", a)
	}
	switch b := a.(type) {
	case nil:
		fmt.Println("nil")
	case *int:
		fmt.Println("Int type:", *b)
	case func(int) string:
		fmt.Println(b(1234))
	case fmt.Stringer:
		fmt.Println(b)
	default:
		fmt.Println("unkown")}}// The final output is: d:1234
Copy the code