1. Interfaces
- An interface is a type (defined by the type keyword)
- An interface defines the behavior specification of an object. Only the specification is defined and not implemented. Specific objects implement the specifications in detail.
- Interfaces limit the methods that structures must implement
1. Interface type
- In the Go language, an interface is a type, an abstract type.
- An interface is a collection of methods, a reflection of duck-type programming. What an interface does is just define a protocol (rule), which I call a washing machine whenever a machine has laundry and shake-dry functions. It doesn't care about attributes (data), it only about behavior (method).
2. Why use interfaces
type Cat struct{} func (c Cat) Say() string { return "cat" } type Dog struct{} func (d Dog) Say() string { return "Wangwang" } func main() { c := Cat{} fmt.Println("cat:", c.Say()) d := Dog{} fmt.Println("Dog:", d.Say()) }
3. Definition of interface
- The Go language promotes Interface-oriented programming.
- Interface Definition Format:
type Interface name interface{ Method 1(Parameter 1,Parameter 2···)(Return value 1,Return Value 2···) Method 2(Parameter 1,Parameter 2···)(Return value 1,Return Value 2···) ···· } # Interface name: Use type to define an interface as a custom type name. When naming an interface in Go Language, an er is usually added after the word. For example, Writer is the interface for writing operations, Stringer is the interface for string functions, etc. Interface names are best used to highlight the type meaning of the interface. # Method Name: This method can be accessed by code other than the package in which the interface is located when the method name is capitalized and the interface type name is capitalized. # Parameter list, return value list: parameter variable names in parameter list and return value list can be omitted.
Example: The interface name of the Write method, which we can usually define as Writer
type writer interface{ Write([]byte) error }
4. Conditions for implementing interfaces
- If a variable implements all the methods specified in the interface, then it implements the interface and can be called a variable of this interface type (that is, calling the variable, which can be defined as an interface type).
- To implement an interface, you must satisfy all the methods in the interface and the defined method format
Example:
type spake interface { spake() } // Defined hit function func hit(animal spake) { animal.spake() } // Define a domain structure type dog struct { name string } // Define a cat structure type cat struct { name string } // dog implements the spake interface func (d dog) spake() { fmt.Printf("%s Wangwang~\n", d.name) } // cat implements the spake interface func (c cat) spake() { fmt.Printf("%s cat~\n", c.name) } func main() { var d1 dog var c1 cat var s1 spake s1 = d1 // Structural instances that satisfy all methods in an interface can be assigned to variables of the interface type hit(s1) var s2 spake s2 = c1 hit(s2) }
Example 2:
type animal interface { move() eat(string) } type cat struct { name string feet int8 } type chicken struct { feet int8 } func (c chicken) move() { fmt.Println("Chicken moves") } func (c chicken) eat(f string) { fmt.Printf("Chicken will eat%s", f) } func (c cat) move() { fmt.Println("Cats can run") } func (c cat) eat(f string) { fmt.Printf("Cat eats%s", f) } func main() { var a1 animal bc := cat{ "White Cat", 4, } a1 = bc a1.eat("Small fish") }
5. Interface type variables
- Interface type variables can store all instances that implement the interface. For example, in the example above, variables of type Sayer can store variables of type Dom and cat.
func main() { var x Sayer // Declare a Sayer-type variable x a := cat{} // Instantiate a cat b := dog{} // Instantiate a dog x = a // cat instances can be directly assigned to x x.say() // cat x = b // dog instances can be directly assigned to x x.say() // Wangwang }
Plus: Look at the code below and enjoy the great use of _here
// From gin framework routergroup.go type IRouter interface{ ... } type RouterGroup struct { ... } var _ IRouter = &RouterGroup{} // Ensure that RouterGroup implements the interface IRouter
6. The difference between value receiver and pointer receiver in implementing interfaces
6.1. Implementing interfaces using value receivers
- When the recipient of a method uses a value, the interface can receive either a pointer or a value
Example: Using the method defined by the value receiver, we found that both the cat structure and the *cat pointer structure can assign variables to the interface. (Because the grammar sugar that evaluates pointer-type variables in the Go language, the cat pointer C2 automatically evaluates *c2 internally.)
type animal interface { eat(string) } type cat struct { name string feet int8 } // Method implements all methods of an interface using a value receiver func (c cat) eat(f string) { fmt.Printf("Cat eats%s\n", f) } func main() { var a1, a2 animal c1 := cat{"White Cat", 4} // cat type c2 := &cat{"Black cat", 4} // *cat type a1 = c1 a2 = c2 fmt.Printf("a1:%#v a2:%#v", a1, a2) a1.eat("fish") a2.eat("Mouse") }
Result
a1:main.cat{name:"White Cat", feet:4} a2:&main.cat{name:"Black cat", feet:4} Cats eat fish Cats eat mice
6.2. Implementing interfaces using pointer receivers
- If the method defined is pointer receiver, then interface variables can only receive pointers.
- Usually the method of incoming is pointer recipient, so this should be noted
Example: The recipient of the eat method is of type *cat, so it is not possible to pass in an instance of type cat to a3, where the interface variable A3 can only store values of type *cat.
type animal interface { eat(string) } type cat struct { name string feet int8 } // Method implements all methods of an interface using a value receiver func (c *cat) eat(f string) { fmt.Printf("Cat eats%s\n", f) } func main() { var a3 animal c3 := cat{"blue cat", 4} a3 = &c3 fmt.Printf("a3:%#V\n ", a3) // A3 is pointer type a3.eat("naughty") }
Result
a3:&main.cat{name:"blue cat", feet:4} Cats eat mischief
7. Relationships between interfaces and types
- Multiple types can implement the same interface
- One type implements multiple interfaces
- The method of an interface does not necessarily need to be fully implemented by one type. The method of an interface can be implemented by embedding other types or structures in the type.
Example 1: One type can implement multiple interfaces, and multiple types can implement the same interface
type eater interface { eat(string) } type mover interface { move() } type cat struct{} type dog struct{} // One type implements multiple interfaces, cat implements eater and mover interfaces func (c cat) eat(f string) { fmt.Printf("Cat eats%s\n", f) } func (c cat) move() { fmt.Println("Cats can run") } // Multiple types can implement the same interface, and cat and domain types both implement eater interfaces func (d dog) eat() { fmt.Println("Fall") }
Example 2: The method of an interface does not necessarily need to be fully implemented by one type. The method of an interface can be implemented by embedding other types or structures in the type.
type eater interface { eat(string) } type cat struct{} // One type implements multiple interfaces, cat implements eater and mover interfaces func (c cat) eat(f string) { fmt.Printf("Cat eats%s\n", f) } // Define a Garfield cat structure that inherits cat properties type jiafeimao struct { cat } func main() { j1 := jiafeimao{} var e1 eater = j1 // jiafeumao references the cat structure and can also implement interfaces e1.eat("fish") }
8. Interface Nesting
- New interfaces can be created by nesting between interfaces.
type eater interface { eat(string) } type mover interface { mover() } // Nesting mover interface and eater interface to form a new interface animal type animal interface{ mover eater sleep() }
9. Empty interface
9.1. Definition of an empty interface
- An empty interface is an interface that does not define any method. Therefore, any type implements an empty interface.
- Variables of an empty interface type can store any type of variable.
- The format is as follows: interface {} // empty interfaces are usually defined in this manner and are generally not named
Example:
// Empty interfaces can be named or unnamed, as f1 and f2 do type kong interface{} func f1(x kong) { fmt.Println(x) } func f2(x interface{}) { fmt.Println(x) } func main() { var ( a int = 1 b string = "abc" ) f1(a) f1(b) f2(a) f2(b) }
Result
1 abc 1 abc
9.2. Application of empty interface
- An empty interface is used as a parameter of a function: an empty interface is used to implement a function parameter that can accept any type.
Example
func f(a ...interface{}) { for _, i := range a { fmt.Printf("%v ", i) } } func main() { f(1, "abc", []string{"a", "b", "c"}) }
Output Results
1 abc [a b c]
- Empty interface as map value
Example: When defining a map, slice, the types of elements passed in are variable and empty interfaces can be used
func main() { var m1 = make(map[string]interface{}, 10) m1["name"] = "Xiaowang" // Character string m1["age"] = 20 // int m1["married"] = true // Boolean m1["hobby"] = []string{"say", "sing", "jump"} // Array, [...] denotes automatic calculation of capacity size fmt.Println(m1) }
Output Results
map[age:20 hobby:[Rap and dance] married:true name:Xiaowang]
10. Type Assertions
- An empty interface can store any type of value, but if a program wants to know what type of data is coming in, a type assertion (used to determine the type of variable we're passing in with an empty interface) is required.
10.1, Interface value
- The value of an interface (referred to as interface value) consists of a specific type and a specific type of value.
- These two parts are called the dynamic type and dynamic value of the interface (that is, the data stored by the interface has two parts, the incoming data type and the value of the data).
Example:
var w io.Writer w = os.Stdout w = new(bytes.Buffer) w = nil
graphic
10.2. How to determine the interface type
- Judgement grammar format: x.(T), the grammar returns two parameters, the first parameter is the variable after x is converted to T type, the second value is a Boolean value, true means the assertion succeeds, false means the assertion fails.
- x: variable of type interface {}
- T: Indicates the possible type of assertion x.
Example 1: The incoming A is of type int, and when a.(string) is output, an error will be raised, and the incoming a of type int does not satisfy a.(string)
func assign(a interface{}) { fmt.Println(a.(string)) } func main(){ assign(12) // Direct error at this time }
Example 2: Judging directly by using if (this kind of comparison is rigid, writing types one by one is inconvenient)
func assign(a interface{}) { fmt.Printf("%T\n", a) k, ok := a.(string) // Returns the value of the interface and the Boolean value of the judgment if !ok { fmt.Println("This is not a string type") } else { fmt.Println("This is a string type", k) } } func main() { assign("abc") }
Output Results
string This is a string type abc
Example 2: Multi-type comparison using switch
func assign(a interface{}) { switch v := a.(type) { case string: fmt.Printf("%v,yes string type\n", v) case int: fmt.Printf("%v,yes int type\n", v) case bool: fmt.Printf("%v,yes bool type\n", v) default: break } } func main() { assign(123) assign("abc") assign(true) }
Output Results
123,yes int type abc,yes string type true,yes bool type