This is an introduction to the interface of GO.
Interface is a type
type I interface {
Get() int
}Copy the code
First, an interface is a type, as you can see from its definition using the type keyword. More accurately, an interface is a type with a set of methods that define the behavior of the interface.
Go allows interfaces without any methods. This type of interface is called an Empty Interface.
If a type implements all methods in an interface, we say that the type implements that interface, so all types implement empty Interface, since any type implements at least zero methods. Go has no explicit keywords to implement interfaces, just the methods that interface contains.
The interface variable stores the value of the implementer
//1 type I interface { Get() int Set(int) } //2 type S struct { Age int } func(s S) Get()int { return s.Age } func(s *S) Set(age int) { s.Age = age } //3 func f(i I){ i.Set(10) fmt.Println(i.Get()) } func main() { s := S{} f(&s) //4 }Copy the code
This code defines interface I in #1, implements two methods defined by I in #2 using struct S, and then defines a function f with type I in #3. S implements two methods defined by I, and says S is the implementer of I. F (&s) completes the use of the interface type.
The important use of interface is reflected in the parameters of function F. If there are multiple types of interface, the values of these types can be directly stored in the variables of interface.
S := s {} var I I = &s // assign s to I ftt.println (i.git ()))Copy the code
It is not hard to see that the interface variable stores the object value of the type that implements the interface. This capability is duck typing. When using interface, there is no need to explicitly declare which interface to implement on struct, only need to implement the method in the corresponding interface, go will automatically check the interface. At runtime, it automatically converts from other types to interfaces. Even if multiple interfaces are implemented, go automatically converts when the corresponding interface is used, which is the magic of interfaces.
How to determine which type is stored in the interface variable
Value, ok := em.(T) := em.(T) := em. Em is a variable of type interface, T represents the type to be asserted, value is the value stored in the interface variable, and OK is a bool indicating whether it is of type T for the assertion.
if t, ok := i.(*S); ok {
fmt.Println("s implements I", t)
}Copy the code
Ok is true if I stores values of Type *S, false is not, this ability is called Type assertions.
If you need to distinguish between multiple types, switch assertions are simpler and more straightforward, and can only be used in switch statements.
switch t := i.(type) {
case *S:
fmt.Println("i store *S", t)
case *R:
fmt.Println("i store *R", t)
}Copy the code
4, empty interface
Interface {} is an empty interface type, as defined above: A type that implements all methods of an interface is said to implement that interface. Empty interfaces have no methods, so all types can be considered to implement interface{}. If you define a function whose arguments are of type interface{}, the function should be able to take any type as its arguments.
func doSomething(v interface{}){
}Copy the code
If the argument v to a function can accept any type, does v represent any type inside the function when called? No, although the argument to the function can take any type, it does not mean that v is of any type. Inside doSomething, v is just an interface type. The reason a function can accept any type is that any type passed to the function when GO is executed is automatically converted to interface{}. If you’re interested in how go converts and how v stores values that can accept any type, look at Russ Cox’s implementation of interface.
Since an empty interface can accept any type of argument, can a slice of type interface{} accept any type of slice?
func printAll(vals []interface{}) { //1
for _, val := range vals {
fmt.Println(val)
}
}
func main(){
names := []string{"stanley", "david", "oscar"}
printAll(names)
}Copy the code
Cannot use names (type []string) as type []interface {} in argument to printAll error, why?
This error indicates that GO does not help us automatically convert slice to interface{} type slice, so there is an error. Go does not convert slice of type interface{}. Why didn’t Go help us automatically convert? I was also curious at first, Finally found the answer in go wiki https://github.com/golang/go/wiki/InterfaceSlice is interface {} take up two words long storage space, one is the methods of data, The length of slice []interface{} is fixed at N*2, but the length of []T is N*sizeof(T). The actual value sizes of the two slices are different. This article only introduces the difference between the two slices.
But we can do this manually.
var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
interfaceSlice[i] = d
}Copy the code
5. How to choose the receiver of the implementer of interface
In our previous example, the call to f is f(&s), which is a pointer to S, why not f(s)? What’s wrong with s? Change it to f of s and execute the code.
cannot use s (type S) as type I in argument to f:
S does not implement I (Set method has pointer receiver)Copy the code
This error means that S does not implement I. What is wrong? The key point is that the receiver of the set method in S is a pointer *S.
The interface definition does not specify whether the implementer’s method receiver is a value receiver or a pointer receiver. The Set receiver of S is a pointer. That is, the receiver of the two methods of I, one is value and the other is pointer. The situation call of F (s) is used to pass f a copy of S. When converting the copy of S to I, A receiver whose copy of s does not satisfy the Set method is a pointer and does not implement I. All functions in GO are passed by value i.e. passed by value.
What if receiver is value and the function is called with pointer?
type I interface {
Get() int
Set(int)
}
type SS struct {
Age int
}
func (s SS) Get() int {
return s.Age
}
func (s SS) Set(age int) {
s.Age = age
}
func f(i I) {
i.Set(10)
fmt.Println(i.Get())
}
func main(){
ss := SS{}
f(&ss) //ponter
f(ss) //value
}Copy the code
The implementer SS of I has a method receiver that is a value receiver. The executing code can see that both pointer and value can be executed correctly.
What are the causes of this phenomenon?
If it is called from a pointer, go will automatically convert it because it always knows what value the pointer points to. If it is called from value, Go will not know the original value of value because value is a copy. Go implicitly converts a pointer to value, but not the other way around.
For methods where the receiver is value, any changes made to the value inside the method do not affect the value seen by the caller, which is called passing by value.
Another example of this is that such from https://play.golang.org/p/TvR758rfre
package main import ( "fmt" ) type Animal interface { Speak() string } type Dog struct { } func (d Dog) Speak() string { return "Woof!" } type Cat struct { } //1 func (c *Cat) Speak() string { return "Meow!" } type Llama struct { } func (l Llama) Speak() string { return "?????" } type JavaProgrammer struct { } func (j JavaProgrammer) Speak() string { return "Design patterns!" } func main() { animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}} for _, animal := range animals { fmt.Println(animal.Speak()) } }Copy the code
#1 Cat’s speak Receiver is a pointer, interface Animal’s slice is a value, Cat’s receiver is inconsistent.
The resources
-
https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/02.6.md
-
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go
-
https://tour.golang.org/methods/15
-
https://www.miek.nl/go/#interfaces
-
https://github.com/golang/go/wiki/InterfaceSlice
-
https://play.golang.org/p/TvR758rfre
-
https://golang.org/doc/effective_go.html#interfaces
-
http://en.wikipedia.org/wiki/Duck_typing
From this issue, we will continue to introduce the confusion and difficulties in learning GO for a period of time. Welcome to pay attention to the discussion.
Click here to see the latest updates and all the code in the article.
Wecatch focuses on quality original technical content