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.Fprint
the 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.Fprint
the first parameter of io.Writer
the function is this interface, so as long as the specific type of this interface is implemented , it can be passed to the fmt.Fprint
function as a parameter , and bytes.Buffer
the io.Writer
interface is implemented , so it can be passed as a parameter To fmt.Fprint
function.
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.Buffer
the interface is implemented io.Writer
, we can w = &b
assign 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 cat
and dog
implemented the interface animal
. When in use, the type of each cat
value c
, the type of dog
the value d
assigned to the interface animal
value a
, respectively, and then performing a
the printInfo
method, 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 invoke
function is added. The function receives an animal
interface type parameter. In the example, when the parameter is passed, it is also passed by cat
the value of the type c
, and the running program can be executed normally. Now let's make a little transformation and use cat
pointers of types &c
as 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)
|
The prompt has already clearly told us that the interface is cat
not implemented animal
because the printInfo
method has a pointer receiver, so cat
the value of the type c
cannot be used as the animal
parameter 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