preface
I think, for you to use object-oriented programming programmers, the term “interface” must be familiar, such as Java interface and c++ virtual base class are the implementation of the interface. But the interface concept in Golang is different from other languages and has its own special features, which we will decipher below.
define
The interface in Go is the signature of a set of methods, which is an important part of the Go language. Simply put, an interface is a combination of Method signatures that define the behavior of an object. Interface is a type defined as follows:
type Person interface {
Eat(food string)}Copy the code
It is defined using the type keyword. More specifically, an interface is a type with a set of methods that define the behavior of the interface. Golang interface definitions cannot contain variables, but are allowed without any methods. This type of interface is called empty Interface.
If a type implements oneinterface
We can say that the type implements thatinterface
So we implemented all of our typesempty interface
Because any type implements at least 0 methods. andgo
Don’t likejava
That requires an explicit keywordinterface
, just need to implementinterface
Contains the method.
Implementing an interface
Let’s take the Java language as an example. In Java, to implement an interface, we need to declare:
public class MyWriter implments io.Writer{}
Copy the code
This means that the implementation of the interface needs to be declared, there are dependency restrictions in code writing, and package dependencies need to be dealt with. However, interface implementation in Go language is implicit, for example:
type error interface {
Error() string
}
type RPCError struct {
Code int64
Message string
}
func (e *RPCError) Error(a) string {
return fmt.Sprintf("%s, code=%d", e.Message, e.Code)
}
Copy the code
In the above code, there is no shadow of the error interface, we just need to implement the error () string method to implement the error interface. In Go, all the methods that implement the interface implicitly implement the interface. When we use the RPCError structure above, we don’t care what interface it implements. Go only checks whether a type implements an interface when passing, returning, and assigning values to variables.
This is a convenient way to write Go without introducing package dependencies. However, the low-level implementation of interface will introduce some problems when dynamic detection:
- Performance deteriorates. Using interface as a function parameter, the behavior is determined dynamically at Runtime. Using a concrete type determines the type at compile time.
- It is not clear which interfaces the struct implements, requiring the use of an IDE or other tool.
Two kinds of interface
Most of you who are just starting out here will be wondering why there are two interfaces, because in Go there are two representations of interfaces. Runtime. iface represents the first interface, the one we implemented above, in which methods are defined; Runtime.eface represents the second interface that does not contain any methods. The second interface is often used in our daily development, so the implementation uses a special type. From a compilation standpoint, Golang does not support generic programming. But you can still use interface{} instead of parameters to implement generics.
Interface internal structure
The Go language divides interface types into two categories based on whether they contain a set of methods:
- Use the Runtime. iface structure to represent the interface that contains the method
- use
runtime.eface
Struct means that does not contain any methodsinterface{}
Type;
The runtime.iface structure is defined in the Go language as follows:
type eface struct { / / 16 bytes
_type *_type
data unsafe.Pointer
}
Copy the code
There are only two Pointers to the underlying data and type. From this type we can also infer that any type of the Go language can be converted to interface.
Another structure used to represent the interface is Runtime. iface, which has Pointers to the raw data, data, but more important is the TAB field of type Runtime. itab.
type iface struct { / / 16 bytes
tab *itab
data unsafe.Pointer
}
Copy the code
Let’s take a look at the two types of interface:
runtime_type
Runtime_type is a runtime representation of the Go language type. Below are structures in the runtime package that contain meta information about many types, such as type size, hash, alignment, and type.
type _type struct {
size uintptr
ptrdata uintptr
hash uint32
tflag tflag
align uint8
fieldAlign uint8
kind uint8
equal func(unsafe.Pointer, unsafe.Pointer) bool
gcdata *byte
str nameOff
ptrToThis typeOff
}
Copy the code
I’ll cover only a few of the more important fields:
-
The size field stores the memory space occupied by the type and provides information about the allocation of memory space.
-
The hash field helps us quickly determine if the types are equal;
-
The equal field, which determines whether multiple objects of the current type are equal, was migrated from the typeAlg structure to reduce the Go language binary package size);
-
runtime_itab
Runtime. itab is a core component of the interface type. Each Runtime. itab takes 32 bytes.
type itab struct { / / 32 bytes
inter *interfacetype
_type *_type
hash uint32
_ [4]byte
fun [1]uintptr
}
Copy the code
“Inter” and “_type” are fields used to indicate types. “hash” is a copy of “_type.” This field can be used to quickly determine whether the target type is consistent with runtime. Fun is a dynamically sized array that is a virtual function table for dynamic distribution, storing a set of function Pointers. Although the variable is declared as a fixed-size array, the raw pointer is used to retrieve the data, so the number of elements held in the FUN array is indeterminate;
Make a brief introduction of the internal structure, interested students can learn by themselves.
The empty interface (runtime.eface
)
Now that we’ve seen what an empty interface is, let’s take a look at how an empty interface is used. Define the function as follows:
func doSomething(v interface{}){}Copy the code
This function takes interface as an input parameter. Note that interface is not an arbitrary type, unlike void * in C. If we convert the type to interface{}, the variable’s type changes at runtime. When you get the variable type, you get interface{}. 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{}.
An interface{} slice can accept any type of slice, since an empty interface can accept any type of slice. Let’s give it a try:
import (
"fmt"
)
func printStr(str []interface{}) {
for _, val := range str {
fmt.Println(val)
}
}
func main(a){
names := []string{"stanley"."david"."oscar"}
printStr(names)
}
Copy the code
/main.go:15:10: Cannot use names (type []string) as type []interface {} in argument to printStr.
I am also very confused here. Why didn’t Go help us to automatically convert slice into interface type slice? We tried to use this method in our previous project, but we failed. Later, I finally found the answer. If you are interested, you can read the original text. Here is a brief summary: Interface will occupy two words of storage space, one is its own methods data, and the other is a pointer to its stored value, that is, the value stored by the interface variable, so the length of slice []interface{} is fixed N*2. But the length of []T is N*sizeof(T), and the actual stored values of the two slices are different.
Since this approach doesn’t work, what can be done about it? We can simply use a slice of an element whose type is interface.
var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
interfaceSlice[i] = d
}
Copy the code
Is not emptyinterface
When the Go language implements interfaces, it can have structure-type methods as well as methods that use pointer types. There are no strict rules in the Go language about whether implementors should use a value type or a pointer, so let’s guess what would happen if we used both value type and pointer type methods to implement an interface?
Let’s start with an example:
package main
import (
"fmt"
)
type Person interface {
GetAge () int
SetAge (int)}type Man struct {
Name string
Age int
}
func(s Man) GetAge(a)int {
return s.Age
}
func(s *Man) SetAge(age int) {
s.Age = age
}
func f(p Person){
p.SetAge(10)
fmt.Println(p.GetAge())
}
func main(a) {
p := Man{}
f(&p)
}
Copy the code
If you look at the code above, do you have any questions about the input parameter in f(&p)? What if you don’t take the address and just send it? /main.go:34:3: Cannot use p (type Man) as type Person in argument to f: Man does not implement Person (SetAge Method has pointer receiver). Since the receiver of SetAge method is a pointer type, f is passed a copy of P. In the conversion of P to person, the copy of P is a pointer type that does not satisfy the SetAge method. And that’s one of the things that’s going on in go is that functions are passed by value.
The above example causes this problem because of value passing. In fact, regardless of whether the receiver type is a value type or a pointer type, it can be called by a value type or a pointer type, which actually works through syntax sugar. Implementing a method whose receiver is a value type is equivalent to automatically implementing a method whose receiver is a pointer type. Methods that implement a receiver type of pointer do not automatically generate methods that correspond to a receiver type of value.
Here’s an example:
type Animal interface {
Walk()
Eat()
}
type Dog struct {
Name string
}
func (d *Dog)Walk(a) {
fmt.Println("go")}func (d *Dog)Eat(a) {
fmt.Println("eat shit")}func main(a) {
var d Animal = &Dog{"nene"}
d.Eat()
d.Walk()
}
Copy the code
This interface defines two functions: Animal
Walk()
Eat()
Copy the code
We then define a structure, Dog, that implements two methods, a value receiver and a pointer receiver. There is no problem if we call the two functions defined using variables of interface type. What if we call them like this:
func main(a) {
var d Animal = Dog{"nene"}
d.Eat()
d.Walk()
}
Copy the code
This is an immediate error, we only changed part of it, the first time &dog {“nene”} was assigned to d; The second time, Dog{“nene”} is assigned to d. The second error is because D didn’t implement Animal. This explains the above conclusion, so when you implement a method whose receiver is a value type, you can automatically generate a method whose receiver is a pointer type, because neither affects the receiver. However, when a method whose receiver is a pointer type is implemented, if at this point a method whose receiver is a value type is automatically generated, the expected change to the receiver (implemented through the pointer) cannot be implemented because the value type makes a copy and doesn’t really affect the caller.
In summary: If you implement methods whose receivers are of value type, you implicitly implement methods whose receivers are of pointer type.
Types of assertions
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. The syntax is summarized as follows:
< value of target type >, < Boolean parameter > := < expression >.(Target type)// Secure type assertion< value of target type > := < expression >.(Target type)// Insecure type assertion
Copy the code
Here’s a simple example:
type Dog struct {
Name string
}
func main(a) {
var d interface{} = new(Dog)
d1,ok := d.(Dog)
if! ok{return
}
fmt.Println(d1)
}
Copy the code
This is a safe type assertion and is more suitable for online code. What if you use an unsafe type assertion?
type Dog struct {
Name string
}
func main(a) {
var d interface{} = new(Dog)
d1 := d.(Dog)
fmt.Println(d1)
}
Copy the code
The following error occurs:
panic: interface conversion: interface {} is *main.Dog, not main.Dog
Copy the code
Assertion failed. Panic occurs directly here, so it is not recommended for online code.
FMT. Println uses type assertions inside the FMT. Println.
The problem
The above introduces the basic use of interface and some possible problems. Here are three questions to see if you have really mastered it.
Problem a
Which line of the following code has a compilation error? (pops)
type Student struct{}func Set(x interface{}){}func Get(x *interface{}){}func main(a) {
s := Student{}
p := &s
// A B C D
Set(s)
Get(s)
Set(p)
Get(p)
}
Copy the code
A, B, C, D, *interface{} *interface{} *interface{} *interface{} *interface{}
Question 2
What is the result of running this code?
func PrintInterface(val interface{}) {
if val == nil {
fmt.Println("this is empty interface")
return
}
fmt.Println("this is non-empty interface")}func main(a) {
var pointer *string = nil
PrintInterface(pointer)
}
Copy the code
Answer: This is non-empty interface. Interface {} is an empty interface type.
type eface struct { / / 16 bytes
_type *_type
data unsafe.Pointer
}
Copy the code
So an implicit conversion occurs when the function PrintInterface is called. In addition to passing parameters to the method, the assignment of variables also triggers an implicit conversion. Eface struct{} is not nil, but the poniter that data points to is nil.
Question 3
What is the result of running this code?
type Animal interface {
Walk()
}
type Dog struct{}
func (d *Dog) Walk(a) {
fmt.Println("walk")}func NewAnimal(a) Animal {
var d *Dog
return d
}
func main(a) {
if NewAnimal() == nil {
fmt.Println("this is empty interface")}else {
fmt.Println("this is non-empty interface")}}Copy the code
Answer: This is a non-empty interface. The interface is a non-empty iface interface.
type iface struct { / / 16 bytes
tab *itab
data unsafe.Pointer
}
Copy the code
Animal interface{} is a null pointer to nil, but return d will trigger a copy of Animal = p. P is nil, just data in iFace is nil. But iface struct{} itself is not nil.
conclusion
Interface is often used in our daily development, so it is necessary to learn it well. I hope this article can give you a new understanding of the interface of Go language. This article ends here, and we will see you next time ~ ~ ~.
Quality three even (share, praise, look) are the author continue to create more quality content motivation!
Set up a Golang communication group, welcome everyone to join, the first time to watch quality articles, not to be missed oh (public account access)
At the end, I will send you a small welfare. Recently, I was reading the book [micro-service architecture design mode], which is very good. I also collected a PDF, which can be downloaded by myself if you need it. Access: Follow the public account: [Golang Dreamworks], background reply: [micro service], can be obtained.
I have translated a GIN Chinese document, which will be maintained regularly. If you need it, you can download it by replying to [GIN] in the background.
Translated a Machinery Chinese document, will be regularly maintained, there is a need for friends to respond to the background [Machinery] can be obtained.
I am Asong, an ordinary program ape, let GI gradually become stronger together. I built my owngolang
Communication group, you need to add mevx
I’ll pull you into the group. We welcome your attention, and we’ll see you next time
Recommended previous articles:
- Mechanics-go Asynchronous task queues
- Leaf-segment Distributed ID Generation System (Golang implementation version)
- 10 GIFs to help you understand sorting algorithms (with go implementation code)
- Go Book Recommendations (From Getting Started to Giving up)
- Go parameter transfer type
- Teach my sister how to write message queues
- Cache avalanche, cache penetration, cache breakdown
- Context package, read this article enough!!
- Go -ElasticSearch: How to get started
- Interviewer: Have you used for-range in go? Can you explain the reasons for these problems
- Learn wire dependency injection, Cron timing task is actually so easy!
- I heard you don’t know how to JWT or swagger. – I’m skipping meals and I’m here with my practice program
- Master these Go features and you will improve your level by N levels