This is a creation in Article, where the information may have evolved or changed.
[TOC]
Golang Interface Interface Introduction
Interface Introduction
If Goroutine and channel are the two cornerstones of go concurrency, then the interface is the key to the data type in the Go language programming. In the real programming of the go language, almost all data structures are expanded around the interface, which is the core of all the data structures in the go language.
Go is not a typical OO language, and it does not support the concept of classes and inheritance in syntax.
Is it impossible to have polymorphic behavior without inheritance? The answer is no, the Go language introduces a new type of-interface, which implements a "polymorphic" concept similar to C + + in effect, although the polymorphism of C + + is not exactly equivalent in syntax, but at least in the final implementation of the effect, it has polymorphic shadow.
Although the go language does not have a class concept, the data types it supports can define the corresponding method (s). In essence, the so-called method (s) is actually a function, but compared with the normal function, such a function is on a data type, so in the function signature, there will be a receiver (receiver) to indicate that the currently defined function is on the receiver.
Any other data type that is supported by the go language can define its method (not only the struct supports method) except for the interface type, except that method (s) is defined more on the struct in the actual project.
From this point of view, we can think of the struct in go as a lightweight "class" that does not support inheritance behavior.
Syntactically, interface defines a method or a group of methods (s) that have only function signatures and no specific implementation code (do you associate virtual functions in C + +?). )。 If a data type implements the functions defined in interface that are called "methods", these data types are said to implement (implement) interface. This is our common oo approach, as shown in a simple example
```type MyInterface interface{ Print()}func TestFunc(x MyInterface) {}type MyStruct struct {}func (me MyStruct) Print() {}func main() { var me MyStruct TestFunc(me)}```
Why Interface
Why use an interface? In the sharing of Gopher China, the great God gave the following reasons:
Writing generic algorithm (generic programming)
Hiding implementation detail (hide concrete implementation)
Providing interception points
Here are a general introduction to these three reasons
Writing generic algorithm (generic programming)
Strictly speaking, generic programming is not supported in Golang. Using generic programming in high-level languages such as C + + is very simple, so generic programming has always been the most golang. But with interface we can implement generic programming, and here's a reference example
"Package sort//a type, typically a collection, that satisfies sort. Interface can be//sorted by the routines in this package. The methods require that the//elements of the collection being enumerated by an integer index.type Interface Interface { Len is the number of elements in the collection. Len () int/Less reports whether the element with//index I should sort before the element with index J. Less (i, J int) bool//Swap swaps the elements with indexes I and J. Swap (i, J int)}...//Sort sorts data.//It makes one call to data. Len to determine N, and O (N*log (n)) calls to//data. Less and data. Swap. The sort is isn't guaranteed to being stable.func sort (data Interface) {//Switch to Heapsort if depth of 2*ceil (LG (N+1)) I S reached. N: = data. Len () MaxDepth: = 0 for I: = N; i > 0; I >>= 1 {maxdepth++} maxDepth *= 2 quickSort (data, 0, N, maxDepth)} "
The formal parameter of the Sort function is a interface that contains three methods: Len (), less (i,j int), Swap (i, J int). When used regardless of the element type of the array (int, float, string ...). ), as long as we implement these three methods we can use the Sort function, thus implementing "generic programming".
This way, I also have a practical application in the Flash Chat project, the specific case is to sort messages.
Here is a specific example, the code can explain everything, a look to understand:
```type Person struct {Name stringAge int}func (p Person) String() string { return fmt.Sprintf("%s: %d", p.Name, p.Age)}// ByAge implements sort.Interface for []Person based on// the Age field.type ByAge []Person //自定义func (a ByAge) Len() int { return len(a) }func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }func main() { people := []Person{ {"Bob", 31}, {"John", 42}, {"Michael", 17}, {"Jenny", 26}, } fmt.Println(people) sort.Sort(ByAge(people)) fmt.Println(people)}```
Hiding implementation detail (hide concrete implementation)
Hide the concrete implementation, this is very good understanding. For example, I design a function for you to return a interface, then you can only through the interface inside the method to do some operations, but the implementation of the internal is completely unknown.
For example, we used the context package, that is, the context is first provided by Google, has now been included in the standard library, and on the basis of the original context added: Cancelctx,timerctx,valuectx.
Just before we have specifically said the context, and now to review
```func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) }}```
Indicates that the upper Withcancel function returns a Context interface, but the specific implementation of this interface is the Cancelctx struct.
"//Newcancelctx returns an initialized cancelctx. Func newcancelctx (parent Context) Cancelctx {return cancelctx{context:parent, Done:mak E (Chan struct{}),}}//A Cancelctx can be canceled. When canceled, it also cancels a children//that implement Canceler. Type cancelctx struct {Context//Note this place done Chan struct{}//Closed by the first cancel call. Mu sync. Mutex children map[canceler]struct{}//set to nil by the first cancel call err error/ /set to Non-nil by the first cancel call} func (c *cancelctx) done () <-chan struct{} {return C.done } func (c *cancelctx) ERR () error {C.mu.lock () defer c.mu.unlock () return C.err} Func (c *cancelctx) string () string {return FMT. Sprintf ("%v.withcancel", C.context)} "
Although the internal implementation of the following three functions returns the specific struct (all implemented by the Context interface) is different, but for the user is completely unaware.
```func WithCancel(parent Context) (ctx Context, cancel CancelFunc) //返回 cancelCtxfunc WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) //返回 timerCtxfunc WithValue(parent Context, key, val interface{}) Context //返回 valueCtx```
Providing interception points
We have no more, we want to add
Interface Source Code Analysis
Say so much, then you can look at the implementation of the specific source code
Interface Bottom Structure
Based on whether the interface contains method, the underlying implementation is represented by two structs: Iface and Eface. Eface represents a interface structure that does not contain method, or is called an empty interface. For most data types in Golang, you can abstract the _type structure, and there is some additional information for different types.
```type eface struct { _type *_type data unsafe.Pointer}type _type struct { size uintptr // type size ptrdata uintptr // size of memory prefix holding all pointers hash uint32 // hash of type; avoids computation in hash tables tflag tflag // extra type information flags align uint8 // alignment of variable with this type fieldalign uint8 // alignment of struct field with this type kind uint8 // enumeration for C alg *typeAlg // algorithm table gcdata *byte // garbage collection data str nameOff // string form ptrToThis typeOff // type for pointer to this type, may be zero}```
Iface represents the underlying implementation of Non-empty interface. Some method is included compared to the empty interface,non-empty. The specific implementation of method is stored in the Itab.fun variable.
```type iface struct { tab *itab data unsafe.Pointer}// layout of Itab known to compilers// allocated in non-garbage-collected memory// Needs to be in sync with// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.type itab struct { inter *interfacetype _type *_type link *itab bad int32 inhash int32 // has this itab been added to hash? fun [1]uintptr // variable sized}```
Imagine, if interface contains multiple method, there is only one fun variable how to save it?
In fact, through the disassembly of the assembly can be seen, the intermediate process compiler will be based on our conversion target type of the empty interface or Non-empty interface, to convert the original data type (converted to <_type, unsafe. Pointer> or <itab, unsafe. pointer>). Here the compiler detects the type requirements for a struct that is not satisfied with the interface (that is, whether the struct implements all interface method).
The Itab of Iface
The most important thing in the IFACE structure is the ITAB structure. Itab can be understood as Pair<interface type, concrete type>. Of course Itab contains some other information, such as the concrete implementation of the method contained in the interface. Here's the detail. The structure of the itab is as follows.
```type itab struct { inter *interfacetype _type *_type link *itab bad int32 inhash int32 // has this itab been added to hash? fun [1]uintptr // variable sized}```
InterfaceType contains some information about the interface itself, such as the package path, which contains the method. The above mentioned Iface and eface are the struct structure of the entity after the data type (built-in and type-define) is converted to interface, and here InterfaceType is an abstraction when we define interface Said.
```type interfacetype struct { typ _type pkgpath name mhdr []imethod}type imethod struct { //这里的 method 只是一种函数声明的抽象,比如 func Print() error name nameOff ityp typeOff}```
_type represents the concrete type. Fun represents the concrete implementation of the method inside the interface. For example, interface type contains method A, B, you can find the specific implementation of these two method by fun.
Interface Memory Layout
It is necessary to understand the memory structure of interface, and only by understanding this can we further analyze the efficiency of situations such as type assertions. Let's look at an example:
```type Stringer interface { String() string}type Binary uint64func (i Binary) String() string { return strconv.Uitob64(i.Get(), 2)}func (i Binary) Get() uint64 { return uint64(i)}func main() { b := Binary{} s := Stringer(b) fmt.Print(s.String())}```
According to the above interface of the source code implementation, you can know, interface in memory is actually composed of two members, for example, the tab point to the virtual table, data point to the actual reference to the database. The virtual table depicts the actual type information and the set of methods required by the interface.
! [Uploading interface memory layout _731644.png]
Observing the structure of the itable, the first is some metadata describing the type information, followed by a list of function pointers satisfying the Stringger interface (note that this is not the set of function pointers for the actual type binary). So if we make a function call through an interface, the actual operation is actually s.tab->fun0. Is it like the virtual table of C + +? Next we'll look at the Golang of the virtual table and the difference between the virtual table of C + +.
Look at C + +, which creates a set of methods for each type, and its virtual table is actually the method set itself or part of it, when faced with multiple inheritance (or to implement multiple interfaces, this is very common), C + + object structure There will be more than one virtual table pointers, Each virtual table pointer points to different parts of the method set, so the function pointers inside the C + + method set are in strict order. Many C + + novice in the face of multiple inheritance becomes the egg pain chrysanthemum tight, because it's this way of design, in order to ensure that its virtual table can work properly, C + + introduced a lot of concepts, what virtual inheritance Ah, interface function The same name problem Ah, the same interface at different levels are inherited multiple problems ah and so on ... It is also easy for the veteran to write the problem code out of negligence.
Let's look at how Golang is implemented, and as with C + +, Golang creates a set of methods for each type, unlike the virtual tables of interfaces that are specifically generated at run time. Perhaps a careful classmate can find out why a virtual table is generated at run time. Because too much, the combination of each interface type and all the entity types that satisfy its interface is the number of possible virtual tables, in fact most of them are not needed, so Golang chooses to generate it at run time, for example, when the example is first met with a statement such as s: = Stringer (b), Golang generates a stringer interface that corresponds to a binary type of virtual table and caches it.
It is easy to understand the Golang memory structure, and then to analyze the efficiency of situations such as type assertions, and when determining whether a type satisfies an interface, Golang uses the set of methods required by the type to match the set of methods needed by the interface, if the set of methods of the type completely contains the set of methods of the interface. You can think of the type as satisfying the interface. For example, if a type has M methods, an interface has n methods, it is easy to know that the time complexity of this decision is O (mXn), but can be optimized in a pre-ordered manner, the actual time complexity is O (m+n).
Comparison of interface and nil
Citing the discussions of colleagues in the company, I feel that I did not understand, for this, listed separately, examples are the best explanation, as follows
package mainimport ( "fmt" "reflect")type State struct{}func testnil1(a, b interface{}) bool { return a == b}func testnil2(a *State, b interface{}) bool { return a == b}func testnil3(a interface{}) bool { return a == nil}func testnil4(a *State) bool { return a == nil}func testnil5(a interface{}) bool { v := reflect.ValueOf(a) return !v.IsValid() || v.IsNil()}func main() { var a *State fmt.Println(testnil1(a, nil)) fmt.Println(testnil2(a, nil)) fmt.Println(testnil3(a)) fmt.Println(testnil4(a)) fmt.Println(testnil5(a))}
The returned results are as follows
falsefalsefalsetruetrue
Why is it?
A variable of type interface{} contains 2 pointers, one pointer to the type of the value, and the other pointer to the actual value
For a interface{} type of nil variable, its two pointers are 0, but when var a *state is passed in, the pointer to the type is not 0, because there is a type, so the comparison is false. Interface type comparison, if two pointers are equal, they can be equal.
Common skills
Pending additions
Parameter handling for
- func: Returns the specific type, receives the interfaces parameter