Interface is a display of the external capabilities of an object. When we use an object, we often do not need to know the complex internal implementation of an object. Through the interface exposed by the object, we can know what capabilities the object has and how to use this capability.

We often say that “Buddha has many faces”. Different people see different Buddha. A complex composite object can often be a generalist, with multiple capabilities and implementations of multiple interfaces in form. “Weak water three thousand, just take one ladle”, we can use this object according to different occasions to select the interface ability to meet the needs.

The interface type of the Go language is very special, serving the same purpose as the Java language interface, but in a very different form. The Java language needs to explicitly implement some interface in the definition of a class before it can be said to be capable of interface definition. But Go interfaces are implicit. As long as the methods defined on the structure are formally the same as those defined on the interface (names, parameters, and return values), then the structure automatically implements the interface and we can use the interface variable to refer to the structure object. Let’s look at an example

package main

import "fmt"/ / can smelltypeSmellable interface {smell()} //typeEatable interface {eat()} // Apples are both possible to smell and eattype Apple struct {}

func (a Apple) smell() {
  fmt.Println("apple can smell")
}

func (a Apple) eat() {
  fmt.Println("apple can eat"} // Flowers can only be smelledtype Flower struct {}

func (f Flower) smell() {
  fmt.Println("flower can smell")
}

func main() {
  var s1 Smellable
  var s2 Eatable
  var apple = Apple{}
  var flower = Flower{}
  s1 = apple
  s1.smell()
  s1 = flower
  s1.smell()
  s2 = apple
  s2.eat()
}

--------------------
apple can smell
flower can smell
apple can eat
Copy the code

The above code defines two interfaces, both of which are implemented by the Apple structure and only the Smellable interface by the Flower structure. Instead of using the implements keyword, which is similar to the Java language, the structure and interface are automatically associated.

Empty interface

If no methods are defined in an interface, it is an empty interface, and any structure implicitly implements an empty interface.

In order to prevent users from repeatedly defining many empty interfaces, Go has built one of its own. This empty interface has a very strange name, interface{}, which beginners will not be used to. The reason the type name is enclosed in braces is to tell the user that there is nothing in the braces. I always thought the name was weird, and it made the code look a little ugly.

The empty interface has no methods in it, so it does not have any capabilities. Its function is equivalent to the Java Object type, which can hold any Object. It is a universal container. For example, if a dictionary key is a String, but you want the value to hold any type of Object, similar to the Java language Map<String,Object> type, you can use the empty interface type interface{}.

package main

import "fmt"

func mainVar user = map[string]interface{}{"age": 30."address": "Beijing Tongzhou"."married": true} FMT.Println(user) var age = user["age"].(int)
	var address = user["address"].(string)
	var married = user["married"].(bool)
	fmt.Println(age, address, married)
}

-------------
map[age:30 address:Beijing Tongzhou married:true]
30 Beijing Tongzhou true
Copy the code

The user dictionary variable is of type MAP [string]interface{}, and the value read directly from this dictionary is of type interface{}.

The nature of interface variables

When we use an interface, we think of it as a special container that can hold only one object, and only objects that implement the interface type can be put in it.

As a variable, interface variables also need to occupy memory space. Through looking through the source code of Go language, it can be found that interface variables are also defined by the structure, which contains two pointer fields, one field points to the contained object memory, the other field points to a special structure ITAB. This particular structure contains the type information of the interface and the data type information of the contained object.

// interface structure
typeUnsafe. int, unsafe. int, unsafe. int, unsafe. int, unsafe. int, unsafe. int, unsafe. int, unsafe. inttypeItab struct {inter *interfacetype // interfacetype information: *_type // data type information... }Copy the code

Since the interface variable contains only two pointer fields, its memory footprint should be two machine words, so let’s write code to verify that

package main

import "fmt"
import "unsafe"

func main() {var s interface{} fpt.println (unsafe.sizeof (s)) var arr = [10]int {1,2,3,4,5,6,7,8,9,10} fmt.Println(unsafe.Sizeof(arr)) s = arr fmt.Println(unsafe.Sizeof(s)) } ---------- 16 80 16Copy the code

The memory footprint of the array is 10 machine words, but this does not affect the memory footprint of the interface variable at all.

Use interfaces to simulate polymorphism

As mentioned earlier, an interface is a special container that can hold many different objects as long as they all implement the methods defined by the interface. If we were to replace the contained object with another object, wouldn’t we be able to accomplish the polymorphism function we missed in the previous section? Ok, so with that in mind, let’s simulate polymorphism

package main

import "fmt"

type Fruitable interface {
	eat()
}

typeFruit struct {Name string Fruitable // Fruitable //want() {
	fmt.Printf("I like ") f.at () // The outer structure automatically inherits methods of anonymous embedded variables}type Apple struct {}

func (a Apple) eat() {
	fmt.Println("eating apple")}type Banana struct {}

func (b Banana) eat() {
	fmt.Println("eating banana")
}

func main() {
	var f1 = Fruit{"Apple", Apple{}}
	var f2 = Fruit{"Banana", Banana{}}
	f1.want()
	f2.want()
}

---------
I like eating apple
I like eating banana
Copy the code

Simulating polymorphism in this way is essentially accomplished by combining attribute variables (Name) and interface variables (Fruitable). Attribute variables are the data of the object, while interface variables are the functions of the object. Combining them together forms a complete polymorphic structure.

Composite inheritance of interfaces

Interface definitions also support composite inheritance. For example, we can combine two interface definitions into one as follows

type Smellable interface {
  smell()
}

type Eatable interface {
  eat()
}

type Fruitable interface {
  Smellable
  Eatable
}
Copy the code

In this case, the Fruitable interface automatically contains smell() and eat() methods, which are equivalent to the following definitions.

type Fruitable interface {
  smell()
  eat()
}
Copy the code

Assignment of interface variables

Assignment to a variable is essentially a shallow copy of memory. Assignment to a slice copies the cut header, assignment to a string copies the string header, and assignment to an array copies the entire array. Will the assignment of interface variables be different? So let’s do an experiment

package main

import "fmt"

type Rect struct {
	Width int
	Height int
}

func main() {
	var a interface {}
	var r = Rect{50, 50}
	a = r

	var rx = a.(Rect)
	r.Width = 100
	r.Height = 100
	fmt.Println(rx)
}

------
{50 50}
Copy the code

It can be inferred from the above output that the structure’s memory was copied, either because of assignment (a = r) or type conversion (rx = a.(Rect)), or both. Is it possible to determine whether memory copy occurred during interface variable assignment? I’m sorry, we haven’t learned enough so far. We’ll use the Unsafe package later in the advanced stages to explore more details. But I can tell you ahead of time what the answer is, which is that in both cases a copy of data memory occurs — a shallow copy.

Interface variable that points to a pointer

If the above example were changed to a pointer, with the interface variable pointing to a structure pointer, the result would be different

package main

import "fmt"

type Rect struct {
	Width int
	Height int
}

func main() { var a interface {} var r = Rect{50, Var rx = a.(*Rect) // convert to a pointer type r.width = 100 r.eight = 100 ftt. Println(rx)} ------- &{100 100}Copy the code

It can be seen from the output that the pointer variable rx points to the same memory as r. This is because only memory copy of the pointer variable occurs during the conversion, and the memory to which the pointer variable points is shared.

Scan the qr code on wechat and follow the code hole to read more chapters of Learn Go