Golang Interface Interface Introduction

Source: Internet
Author: User
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
    1. func: Returns the specific type, receives the interfaces parameter
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.