Go language combat notes (9) | Go interface

Interface is a kind of convention, it is an abstract type, different from the concrete types we see such as int, map, slice, etc. The specific type, we can know what it is and what can be done with it; but the interface is different, the interface is abstract, it has only a set of interface methods, we don’t know its internal implementation, so we don’t know What is the interface, but we know what we can do with the methods it provides.

Abstraction is the advantage of the interface. It does not need to be bound to the specific implementation details. We only need to define the interface and tell the coders what it can do, so that we can separate the specific implementation, so that the coding will be more flexible and adaptable. It will also be very strong.

func main() {
	var b bytes.Buffer
	fmt.Fprint(&b,"Hello World")
	fmt.Println(b.String())
}
   

The above is an example of using the interface, let's first look at fmt.Fprintthe implementation of the function.

func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
	p := newPrinter()
	p.doPrint(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}
   

From the source code above, we can see that fmt.Fprintthe first parameter of io.Writerthe function is this interface, so as long as the specific type of this interface is implemented , it can be passed to the fmt.Fprintfunction as a parameter , and bytes.Bufferthe io.Writerinterface is implemented , so it can be passed as a parameter To fmt.Fprintfunction.

Internal realization

We mentioned earlier that the interface is used to define the type of behavior. It is abstract. These defined behaviors are not implemented directly by the interface, but by user-defined types through methods. If the user-defined type implements all the methods declared by the interface type, then the user-defined type implements the interface, so the value of the user-defined type can be assigned to the value of the interface type.

func main() {
	var b bytes.Buffer
	fmt.Fprint(&b, "Hello World")
	var w io.Writer
	w = &b
	fmt.Println(w)
}
   

In this example, because bytes.Bufferthe interface is implemented io.Writer, we can w = &bassign a value. This assignment operation will store the value of the defined type into the value of the interface type.

After the assignment operation is executed, if we call the interface method, it is actually calling the corresponding method of the stored user-defined type. Here we can call the user-defined type 实体类型.

We can define many types and let them implement an interface. Then these types can be assigned to this interface. At this time, the call of the interface method is actually 实体类型the call of the corresponding method, which is polymorphism.

func main() {
	var a animal

	var c cat
	a=c
	a.printInfo()
	//使用另外一个类型赋值
	var d dog
	a=d
	a.printInfo()
}

type animal interface {
	printInfo()
}

type cat int
type dog int

func (c cat) printInfo(){
	fmt.Println("a cat")
}

func (d dog) printInfo(){
	fmt.Println("a dog")
}
   

The above example demonstrates a polymorphism. We defined an interface animal, then defined two types catand dogimplemented the interface animal. When in use, the type of each catvalue c, the type of dogthe value dassigned to the interface animalvalue a, respectively, and then performing athe printInfomethod, different output can be seen.

a cat
a dog
   

Let's look at the internal layout of the interface value after the interface value is assigned. Value of the interface is a data structure of the word length, the first word of the table contains a pointer to the internal structure, this internal table has stored 实体类型information and associated methodologies; second word is included in a Pointer to the stored 实体类型value. So the value structure of the interface is actually two pointers, which also shows that the interface is actually a reference type.

Method set

We all know that if you want to implement an interface, you must implement all the methods provided by this interface, but when implementing methods, we can use pointer receivers to implement, or value receivers. There is a difference between the two. Let us analyze the difference between the two.

func main() {
	var c cat
	//值作为参数传递
	invoke(c)
}
//需要一个animal接口作为参数
func invoke(a animal){
	a.printInfo()
}

type animal interface {
	printInfo()
}

type cat int

//值接收者实现animal接口
func (c cat) printInfo(){
	fmt.Println("a cat")
}
   

The original example is changed, and a invokefunction is added. The function receives an animalinterface type parameter. In the example, when the parameter is passed, it is also passed by catthe value of the type c, and the running program can be executed normally. Now let's make a little transformation and use catpointers of types &cas parameters.

func main() {
	var c cat
	//指针作为参数传递
	invoke(&c)
}
   

Only modify this one, the others remain unchanged, we run the program and found that it can be executed normally. Through this example, we can draw the conclusion that when an entity type implements an interface as a value receiver, whether it is the value of the entity type or the pointer of the entity type value, the interface is implemented .

Let's try changing the receiver to a pointer.

func main() {
	var c cat
	//值作为参数传递
	invoke(c)
}
//需要一个animal接口作为参数
func invoke(a animal){
	a.printInfo()
}

type animal interface {
	printInfo()
}

type cat int

//指针接收者实现animal接口
func (c *cat) printInfo(){
	fmt.Println("a cat")
}
   

In this example, the receiver that implements the interface is changed to a pointer, but when passing parameters, we still pass by value. Click to run the program, and the following exception prompt will appear:

./main.go:10: cannot use c (type cat) as type animal in argument to invoke:
	cat does not implement animal (printInfo method has pointer receiver)
1
2
 

The prompt has already clearly told us that the interface is catnot implemented animalbecause the printInfomethod has a pointer receiver, so catthe value of the type ccannot be used as the animalparameter of the interface type . Let's modify it slightly below, and pass it as a parameter instead.

func main() {
	var c cat
	//指针作为参数传递
	invoke(&c)
}
   

Everything else is the same, just change the parameter that used the value before to use the pointer as the parameter, we run the program again, and it can run normally. It can be seen that when an entity type implements an interface with a pointer receiver, only a pointer to this type is considered to implement the interface

Now we summarize these two rules, first from the perspective of whether the receiver of the method is a value or a pointer.

Methods Receivers Values
(t T) T and *T
(t *T) *T

The above table can be interpreted as: if it is a value receiver, both the value and pointer of the entity type can implement the corresponding interface; if it is a pointer receiver, only the pointer of the type can implement the corresponding interface.

Secondly, we look at whether the entity type is a value or a pointer.

Values Methods Receivers
T (t T)
*T (t T) and (t *T)

The above table can be interpreted as: the value of the type can only realize the interface of the value receiver; the pointer to the type can realize the interface of the value receiver and the interface of the pointer receiver.

 

See: https://www.flysnow.org/2017/04/03/go-in-action-go-interface.html

Guess you like

Origin blog.csdn.net/qq_32907195/article/details/112394738