This is the 11th day of my participation in the August More Text Challenge

Directory:

  • 01. Why do you choose Go
  • 【 hand by hand teach you to write 】02
  • 03. Basic data types
  • 04. Operators and flow control
  • 05. Function
  • 06. Project management
  • 07. Compound types – Pointers
  • 08. Compound types – arrays and slices
  • 08. Complex types -map
  • 09. Complex types – Structures
  • 10. Object-oriented programming
  • 11. Exception handling
  • 12. Text processing
  • 13. Concurrent programming – concurrent entry

8.1 an overview of the

The Go support language for object-oriented programming is designed to be very simple and elegant. Because Go does not follow many of the concepts of traditional object-oriented programming, such as inheritance (inheritance is not supported, although anonymous fields have similar memory layout and behavior but are not inheritance), virtual functions, constructors and destructors, the hidden this pointer, and so on.

Although there are no concepts of encapsulation, inheritance, or polymorphism in Go, these features are also implemented in other ways:

  • L Encapsulation: implemented by method
  • L Inheritance: implemented through anonymous fields
  • L Polymorphisms: implemented through interfaces

8.2 Anonymous Combination

8.2.1 Anonymous Fields

Normally, the structure is defined in a one-to-one correspondence between the field name and its type. In fact, Go supports the method of providing only the type without writing the field name, that is, anonymous fields, also known as embedded fields.

When an anonymous field is also a structure, all fields owned by that structure are implicitly imported into the currently defined structure.

Type Person struct {name string sex byte age int} type Student struct {Person, So by default Student contains all of the field id of Person int addr String}Copy the code

8.2.2 initialization

Type Person struct {name string sex byte age int} type Student struct {Person, Func main() {s1 := Student{Person{"mike", 'm', 1} "sz"} //s1 = {Person:{name:mike sex:109 age:18} id:1 addr:sz} fmt.Printf("s1 = %+v\n", s1) //s2 := Student{"mike", 'm', S3 := Student{Person: Person{"lily", 'f', 19}, id: Printf("s3 = %+v\n", s3) 2 s4 := Student{Person: {Person:{name: sex:102 age:19} id:2 addr:} FMT.Printf("s3 = %+v\n", s3) Person{name: "tom"}, id: 3} //s4 = {Person:{name:tom sex:0 age:0} id:3 addr:} fmt.Printf("s4 = %+v\n", s4) }Copy the code

8.2.3 Member Operations

Name = "mike" s1.sex = 'm' s1.age = 18 s1.id = 1 s1.addr Person = Person{"lily", 'f', Person = Person{"lily", 'f', 19} s2.id = 2 s2.addr = "bj" fmt.Println(s2) //{{lily 102 19} 2 bj}Copy the code

8.2.4 Fields with the same name

Type Person struct {name string sex byte age int} type Student struct {Person, Int addr String name string name} func main() {var s Student // variable declaration // Assign name to Student or Person? s.name = "mike" //{Person:{name: sex:0 age:0} id:0 addr: Name: Mike} fmt.Printf("%+v\n", s) // Only the outermost member is assigned by default // Anonymous members with the same name are assigned, Name = "yoyo" //Person:{name:yoyo sex:0 age:0} id:0 addr: name:mike} ftt.printf ("%+v\n", s)}Copy the code

8.2.5 Other Anonymous Fields

8.2.5.1 Non-structure types

All built-in and custom types can be used as anonymous fields:

Type Person struct {name string sex byte age int} type Student struct {Person // Struct type int // anonymous field, built-in type mystr // anonymous field, S1 := Student{Person{"mike", 'm', 18}, 1, "Bj "} //{Person:{name:mike sex:109 age:18} int:1 mystr:bj} ftt. Printf("%+v\n", s1) mike, m, 18, 1, bj fmt.Printf("%s, %c, %d, %d, %s\n", s1.name, s1.sex, s1.age, s1.int, s1.mystr) }Copy the code

8.2.5.2 Structure pointer types

Type Person struct {// Person name string sex byte age int} type Student struct {// Person * / Func main() {s1 := Student{&Person{"mike", 'm', 18}, 1, "bj"} //{Person:0xc0420023e0 id:1 addr:bj} fmt.Printf("%+v\n", s1) //mike, m, 18 fmt.Printf("%s, %c, %d\n", s1.name, s1.sex, S1.age) var s2 Student s2.person = new(Person) s2.name = "yoyo" s2.sex = 'f' s2.age = 20 s2.id = 2 s2.addr  = "sz" //yoyo 102 20 2 20 fmt.Println(s2.name, s2.sex, s2.age, s2.id, s2.age) }Copy the code

8.3 methods

8.3.1 overview

In object-oriented programming, an object is really just a simple value or a variable, and the object contains some functions, such functions with receivers, we call methods. In essence, a method is a function associated with a particular type.

An object-oriented program uses methods to express its properties and corresponding operations, so that users of the object do not need to manipulate the object directly, but do so through methods.

In the Go language, you can add methods to any custom type (including built-in types, but not pointer types).

The method always binds an object instance and implicitly takes the instance as the first argument (receiver). The syntax for the method is as follows:

func (receiver ReceiverType) funcName(parameters) (results)

  • L The receiver parameter can be named arbitrarily. Omit the parameter name if it is not used in the method.
  • L The receiver type can be T or *T. The base type T cannot be an interface or pointer.
  • L does not support overloaded methods, that is, methods with the same name but different parameters cannot be defined.

8.3.2 Adding methods for types

8.3.2.1 Base Type as receiver

// When a function is defined, place a variable before its name, Func (a MyInt) Add(b MyInt) MyInt {// Object-oriented return a + b} // traditional way of defining func Add(a, Func main() {var a MyInt = 1 var b MyInt = 1 var b MyInt = 1 var b MyInt = 1 A.A FMT. Println (" dd (b) = ", a.A dd (b)) / / a.A dd (b) = 2 / / calls func Add (a, b MyInt) FMT. Println (" Add (a, b) = ", Add (a, b)) //Add(a, b) = 2 }Copy the code

As you can see from the examples above, object orientation is just a syntactic expression. The method is the syntactic sugar of the function, because receiver is actually the first argument received by the method.

Note: Although the name of the method is identical, the method is different if the receiver is different.

8.3.2.2 Structure as receiver

The receiver’s fields can be accessed from inside the method, and the calling method is accessed through the dot (.), just as fields are accessed from inside the struct:

Func (p Person) PrintInfo() {type Person struct {name string sex byte age int} func (p Person) PrintInfo() { P.ex, p.age)} func main() {p := Person{"mike", 'm', 18}Copy the code

8.3.3 Value semantics and reference semantics

Type Person struct {name string sex byte age int} Func (p *Person) SetInfoPointer() {// Assign a value to the member (*p).name = "yoyo" p.ex = 'f' p.age = 22} Func (p Person) SetInfoValue() {// Assign value to member p.name = "yoyo" p.ex = 'f' p.age = 22} func main() {// pointer as receiver, Person{"mike", 'm', 18} // initialize fmt.Println(" before function call = ", P1) // before function call = {mike 109 18} (&p1).setinfopointer () fmt.println (" after function call = ", (p1) / / after the function call = 102 22} {yoyo FMT. Println (" = = = = = = = = = = = = = = = = = = = = = = = = = = ") p2: = Person {" mike ", 'm', 18} initialization / / / / values as the receiver, Println(" after function = ", p2) // Before function = {Mike 109 18} p2.setInfoValue () fmt.println (" after function = ", p2) // Before function = {Mike 109 18} p2.setInfoValue () fmt.println (" after function = ", p2) P2) // after function call = {mike 109 18}}Copy the code

8.3.4 method set

The method set of a type is the set of all methods that can be called by a value of that type.

Methods (including anonymous fields) called with instance instance value and pointer are not bound by the method set. The compiler compiler always looks up all methods and automatically converts receiver arguments.

*8.3.4.1 Method set of type T

A pointer to a value of a custom type whose set of methods consists of all the methods defined by that type, whether they accept a value or a pointer.

If a method that accepts a value is called on a pointer, the Go language wisely dereferences the pointer and treats the underlying value to which the pointer points as the receiver of the method.

The type *T method set contains all receiver T + *T methods:

Type Person struct {name string sex byte age int} Func (p *Person) SetInfoPointer() {(*p).name = "yoyo" p.ex = 'f' p.age = 22} // value as receiver, Func (p Person) SetInfoValue() {p.name = "XXX" p.ex = 'm' p.age = 33} func main() {//p is the pointer type var p *Person = &Person{"mike", 'm', 18} p.SetInfoPointer() //func (p) SetInfoPointer() p.SetInfoValue() //func (*p) SetInfoValue() (*p).SetInfoValue() //func (*p) SetInfoValue() }Copy the code

8.3.4.2 Method set of Type T

A set of methods that define a value of a custom type consists of methods defined for that type whose receiver type is a value type, but does not include those whose receiver type is a pointer.

But this limitation is usually not as stated here, because if we have only one value, we can still call a method with a pointer receiver, which can be made possible by the address capability of the Go language to pass values.

Type Person struct {name string sex byte age int} Func (p *Person) SetInfoPointer() {(*p).name = "yoyo" p.ex = 'f' p.age = 22} // value as receiver, Func (p Person) SetInfoValue() {p.name = "XXX" p.ex = 'm' p.age = 33} func main() {//p is a common value type var p Person = Person{"mike", 'm', 18} (&p).SetInfoPointer() //func (&p) SetInfoPointer() p.SetInfoPointer() //func (&p) SetInfoPointer() p.SetInfoValue() //func (p) SetInfoValue() (&p).SetInfoValue() //func (*&p) SetInfoValue() }Copy the code

8.3.5 Anonymous Fields

8.3.5.1 Method inheritance

If an anonymous field implements a method, the struct containing the anonymous field can also call that method.

Type Person struct {name string sex byte age int} //Person defines method func (p *Person) PrintInfo() { Printf("%s,%c,%d\n", p.name, p.ex, p.age)} type Student struct {Person, Func main() {p := Person{"mike", 'm', 18} p.PrintInfo() s := Student{Person{"yoyo", 'f', 20}, 2, "sz"} s.PrintInfo() }Copy the code

8.3.5.2 Method rewrite

Func (p *Person) PrintInfo() {FMT.Printf("Person: %s,%c,%d\n", p.name, p.ex, p.age)} type Student struct {Person, Func (s *Student) PrintInfo() {fmt.printf ("Student: func (s *Student) PrintInfo() {fmt.printf ("Student: %s,%c,%d\n", s.name, s.sex, s.age) } func main() { p := Person{"mike", 'm', 18} p.PrintInfo() //Person: Mike,m,18 s := Student{Person{"yoyo", 'f', 20}, 2, "sz"} s.prininfo () //Student: yoyo,f,20 s.Person.PrintInfo() //Person: yoyo,f,20 }Copy the code

8.3.6 expression

Just as we can assign and pass functions, methods can also assign and pass.

Methods come in two forms, depending on the caller: method values and method expressions. Both can be assigned and taken as normal functions, except that method values are bound to instances, whereas method expressions must be explicitly taken as arguments.

8.3.6.1 method value

type Person struct { name string sex byte age int } func (p *Person) PrintInfoPointer() { fmt.Printf("%p, %v\n", p, p) } func (p Person) PrintInfoValue() { fmt.Printf("%p, %v\n", &p, p) } func main() { p := Person{"mike", 'm', 8} p.printinfopointer () // 0xC0420023e0, &{mike 109 18} pFunc1 := p.printinfopointer, Implicit transfer receiver pFunc1() // 0xC0420023e0, &{mike 109 18} pFunc2 := p.prininfoValue pFunc2() // 0xC042048420, {mike 109 18} }Copy the code

8.3.6.2 Method expressions

type Person struct { name string sex byte age int } func (p *Person) PrintInfoPointer() { fmt.Printf("%p, %v\n", p, p) } func (p Person) PrintInfoValue() { fmt.Printf("%p, %v\n", &p, p) } func main() { p := Person{"mike", 'm', 18} p.printinfopointer () // 0xC0420023e0, &{mike 109 18} //func pFunc1(p *Person)) pFunc1 := (*Person).printInfopointer pFunc1(&p) // 0xC0420023e0, &{mike 109 18} pFunc2 := Person.PrintInfoValue pFunc2(p) //0xc042002460, {mike 109 18} }Copy the code

8.4 interface

8.4.1 overview

In Go, an interface is a custom type that describes a collection of methods.

An interface type is an abstract type that does not expose the structure of the internal values of the object it represents and the collection of underlying operations that the object supports, but only their own methods. Therefore, interface types cannot be instantiated.

Go implements duck-typing through the interface: “When a bird is seen walking like a duck, swimming like a duck, and quacking like a duck, that bird can be called a duck”. We don’t care what type of object it is, whether it’s a duck or not, we just care about the behavior.

Interfaces are pure virtual functions that define methods but do not implement them. Interface oriented programming just provides the method to the outside world. You don’t care how you implement it, you don’t care how many versions you implement, you just care which version you want to use.

8.4.2 Using interfaces

8.4.2.1 Interface Definition

type Humaner interface {
    SayHi()
}
Copy the code
  • L Interface names end with er
  • L The interface has only method declarations, no implementations, and no data fields
  • L Interfaces can be anonymously embedded into other interfaces, or embedded into structures

8.4.2.2 Interface Implementation

An interface is a type used to define behavior. These defined behaviors are not implemented directly by the interface, but by methods of user-defined types. A concrete type that implements these methods is an instance of the interface type.

If a user-defined type implements a set of methods declared by an interface type, the value of that user-defined type can be assigned to the value of that interface type. This assignment stores the value of the user-defined type into the value of the interface type.

Type Humaner interface {SayHi()} type Student struct {//Student name string Score float64 *Student) SayHi() { fmt.Printf("Student[%s, %f] say hi!! \n", s.name, SayHi(t *Teacher) SayHi() {SayHi(t *Teacher) SayHi() {SayHi(t *Teacher) SayHi() fmt.Printf("Teacher[%s, %s] say hi!! //MyStr SayHi() func (STR MyStr) SayHi() {fmt.printf ("MyStr[%s] say hi!! \n", STR)} I func WhoSayHi(I Humaner) {i.sayhi ()} func main() {s := &Student{"mike", T := &teacher {"yoyo", "Go "} var TMP MyStr = "sayhi () //Student[mike, 88.880000] say hi!! T.sayhi () //Teacher[yoyo, Go] say hi!! TMP.SayHi() //MyStr[test] say hi!! //Student[Mike, 88.880000] say hi!! WhoSayHi(t) //Teacher[yoyo, Go language] say hi! WhoSayHi(TMP) //MyStr say hi!! X := make([]Humaner, 3) // X [0], x[1], x[2] = s, t, TMP for _, value := range x {value.sayhi ()} /* Student[Mike, 88.880000] say hi!!!!! Teacher[yoyo, Go] say hi! MyStr[查 看] say hi!! * /}Copy the code

From the above code, you can see that an interface is simply a collection of abstract methods that must be implemented by other non-interface types and cannot be self-implementing.

8.4.3 Interface Composition

8.4.3.1 Interface Embedding

If an interface1 is an embedded field in interface2, then interface2 implicitly contains the methods in Interface1.

Type Humaner interface {SayHi()} type Personer interface {Humaner Sing(lyrics string)} type Student Struct {//Student name string score float64} //Student implement SayHi() func (s *Student) SayHi() {fmt.printf ("Student[%s, %f] say hi!! //Student implement Sing() method func (s *Student) Sing(lyrics string) {FMT.Printf("Student Sing [%s]!! \n", lyrics)} func main() {s := &student {"mike", 88.88} var i2 Personer i2 = si2. SayHi() //Student[mike, 88.880000] say hi!!!!! I2.Sing(" Student brother ") //Student brother (" Student brother ") }Copy the code

8.4.3.2 Interface Conversion

Superset interface object can be converted to subset interface, otherwise error:

Type Humaner interface {SayHi()} type Personer interface {Humaner Sing(lyrics string)} type Student Struct {//Student name string score float64} //Student implement SayHi() func (s *Student) SayHi() {fmt.printf ("Student[%s, %f] say hi!! //Student implement Sing() method func (s *Student) Sing(lyrics string) {FMT.Printf("Student Sing [%s]!! \n", lyrics)} func main() {//var i1 Humaner = &student {"mike", 88.88} //var i2 Personer = i1 //err //Personer is superset, Var i1 Personer = &student {"mike", 88.88} var i2 Humaner = &student {"mike", 88.88} var i2 Humaner = &student () //Student[mike, 88.880000] say hi!! }Copy the code

8.4.4 empty interface

The empty interface (interface{}) does not contain any methods, and because of this, all types implement the empty interface, so it can store any type of value. It is somewhat similar to the void * type in C.

Var v1 interface{} = 1 var v2 interface{} = "ABC" var v2 interface{} = "ABC" var v3 interface{} Var v5 interface{} = &struct{X int}{1} var v5 interface{} = &struct{X int}{1}Copy the code

When a function can accept any object instance, we declare it as interface{}. The most typical example is the PrintXXX family of functions in the FMT library, for example:

func Printf(fmt string, args … interface{})

func Println(args … interface{})

8.4.5 Type Query

We know that an interface variable can store any type of value (the type that implements interface). So how do we know in reverse what type of object this variable actually holds? There are two methods commonly used at present:

L – ok assertion

L switch test

8.4.5.1 – ok assertion

Value, ok = Element.(T), where value is the value of the variable, OK is a bool, element is an interface variable, and T is the type of the assertion.

Ok returns true if element does store a value of type T, false otherwise.

Sample code:

type Element interface{} type Person struct { name string age int } func main() { list := make([]Element, 3) list[0] = 1 // an int list[1] = "Hello" // a string list[2] = Person{"mike", 18} for index, element := range list { if value, ok := element.(int); ok { fmt.Printf("list[%d] is an int and its value is %d\n", index, value) } else if value, ok := element.(string); ok { fmt.Printf("list[%d] is a string and its value is %s\n", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d] is a Person and its value is [%s, %d]\n", index, value.name, Value. Age)} else {FMT.Printf("list[%d] is of a different type\n", index)}} /*  list[0] is an int and its value is 1 list[1] is a string and its value is Hello list[2] is a Person and its value is [mike, 18] */ }Copy the code

8.4.5.2 switch test

type Element interface{}
 
type Person struct {
    name string
    age  int
}
 
func main() {
    list := make([]Element, 3)
    list[0] = 1       //an int
    list[1] = "Hello" //a string
    list[2] = Person{"mike", 18}
 
    for index, element := range list {
        switch value := element.(type) {
        case int:
            fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
        case string:
            fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
        case Person:
            fmt.Printf("list[%d] is a Person and its value is [%s, %d]\n", index, value.name, value.age)
        default:
            fmt.Println("list[%d] is of a different type", index)
        }
    }
}
Copy the code