Writing in the front

In the last article “Golang introductory learning structure (struct)”, we learned the knowledge point of structure (struct) in Golang, next we will learn the method in Golang.

Method definition

In Golang, a method is a function that acts on a receiver, which is a variable of some type. Therefore, methods are a special kind of function. Here the receiver can be (almost, the receiver type can’t be an interface type or pointer type) any type, not just a structure type, which means that almost any type can be a method, even a function type, or an alias type for int, bool, etc.

We can think of it this way: a type (say, a structure) with its methods is equivalent to a class in an object-oriented language.

Method definition format

func (recv receiver_type) methodName(parameter_list) (return_value_list){... }Copy the code

Recv is like this or self in object-oriented languages, but Golang doesn’t have those keywords. You can use this or self as the receiver’s name, depending on your preference.

Pay attention to the point

In Golang, the code for the type and the code for the method bound to it may not be placed together; they may exist in different source files, with the only requirement that they belong to the same package. Consider the following example:

We are in the SRC/go_code/method/model/immortal in go type defines a cultivate immortality

package model

/ / cultivate immortality
type immortal struct {
	name   string
	age    int
	gender string
}
Copy the code

Then, we in the SRC/go_code/method/model/immortal_method. In go to define immortal type of method

package model

// Factory function
func NewImmortal(age int, name, gender string) *immortal {

	return &immortal{
		name:   name,
		age:    age,
		gender: gender,
	}
}

// Getter
func (recv *immortal) GetName(a) string {
	return recv.name
}
...
Copy the code

Then, we use it in the main package

package main

import (
	"fmt"
	"go_code/method/model"
)

func main(a) {
	i := model.NewImmortal(18.Mr.han ""."Male")
	name :=i.GetName()
	fmt.Println(name)
}
Copy the code

Output:

mr.hanCopy the code

The difference between functions and methods

Both functions and methods are reusable pieces of code. The difference is that functions are procedure oriented and methods are object oriented. In terms of invocation, functions are called by function names, while methods are called by variables associated with the instance.

// Function call
println("Hello World")
// method call
immortal := model.NewImmortal(18.Mr.han ""."Male")
immortal.GetName()
Copy the code

In Golang, methods are made up of receiver type, method name, parameter list, return value list, and method body, and the receiver must have an explicit name, which must be used in the method. Also, the receiver type (receiver_type) must be declared in the same package as the method.

Additional features of methods in Golang

In Golang, methods that receive type associations are not written in type structures (methods in the object-oriented language Java are defined in classes). As a result, the coupling between method and receiver type in Golang is much looser, that is, the data (fields) are independent of their corresponding behavior.

Can the receiver type be a pointer to a value rather than a type?

Can the receiver type be a pointer to a value rather than a type? The answer is yes. However, I don’t recommend this for performance reasons. Because the receiver is passed to the corresponding method as a value, this is equivalent to passing a copy of the instance’s value to the method, which is not a good deal. In the example below, the receiver could be the value of the instance.

// Immortal level
type Level struct {
	level      string
	levelValue int
}

// Get the level description
func (recv Level) GetLevel(a) string{
	return recv.level
}

func main{
    level := model.Level{"Nine layers of qi training".9200}
	fmt.Println(level.GetLevel())
}
Copy the code

Output:

Nine practice the spirit layerCopy the code

Note:

Both pointer and value methods can be called on Pointers or non-pointers. As shown in the program below, type Level has a method GetLevel() on a value and a method SetLevel() on a pointer, but you can see that both methods can be called on variables of both types.

package model


// Immortal level
type Level struct {
	level      string
	levelValue int
}

func NewLevel(level string, levelValue int) Level {
	return Level{
		level:      level,
		levelValue: levelValue,
	}
}


// Get the level description
func (recv Level) Level(a) string{
	return recv.level
}

func (recv *Level) SetLevel(level string) {
	recv.level = level
}


Copy the code
package main

import (
	"fmt"
	"go_code/method/model"
)

func main(a) {

	level := model.NewLevel("Nine layers of qi training".9200)
	levelPointer := & level
	fmt.Println("Before advancing:",level.Level())
	levelPointer.SetLevel("Perfect gas refining")
	fmt.Println("After promotion:",level.Level())
}


Copy the code

Output:

Before promotion: Gas training after promotion: gas refining great completionCopy the code

Method and unexported fields

In the example above, fields of type Level are invisible to the outside of the package (think of it as private properties in object-oriented languages). Therefore, an error will be reported if the main package is accessed directly through the selector. We can do this through a well-known technique in object-oriented languages: providing getters and setters. In Golang, the Set prefix is used for setter methods and only the member name is used for getter methods.

About concurrent access to objects

Object fields (attributes) should not be changed by two or more different threads at the same time. If this happens in your program, you can use the methods in package sync (such as adding a mutex) to secure concurrent access. But this is not a recommended option (we’ll learn to explore a new approach through Goroutines and Channels later). Take a look at the following example

src/go_code/method/model/level_lock.go

package model

import "sync"

// Immortal level
type levelLock struct {
	Lock sync.Mutex
	level      string
	levelValue int
}

func NewLevelLock(level string, levelValue int) *levelLock {
	return &levelLock{
		level:      level,
		levelValue: levelValue,
	}
}

func (recv *levelLock) SetLevel(level string) {

	recv.level  = level

}
Copy the code

src/go_code/struct/main/level_lock.go

package main

import "go_code/method/model"

func main(a) {
	level := model.NewLevelLock("Nine layers of qi training".9200)
	/ / acquiring a lock
	level.Lock.Lock()
	/ / modify the value
	level.SetLevel("Perfect qi training")
	/ / releases the lock
	defer level.Lock.Unlock()
}

Copy the code

Methods and inheritance of inline types

When an anonymous type is embedded in a structure, the anonymous type’s visible methods are also embedded, in effect the same way that the outer type inherits these methods: it implements the subtype by putting the parent type in a child type. This mechanism provides a simple way to emulate the effects of subclasses and inheritance in classical object-oriented languages. Because multiple anonymous types can be embedded in a structure, we can actually have a simple version of multiple inheritance.

Define an immortal2 type in the Model package and embed it with an anonymous type level

src/go_code/method/model/anonymous_type.go:

package model



/ / cultivate immortality
type immortal2 struct {
	name   string
	age    int
	gender string
	Level
	lingGen
}

func NewImmortal2(age int, name, gender string,levelName string,levelValue int,lingGenNames...string) *immortal2 {
	return &immortal2{
		name:   name,
		age:    age,
		gender: gender,
		Level:  Level{levelName,levelValue},
		lingGen: lingGen{linGenNames: lingGenNames},
	}
}

Copy the code

src/go_code/method/model/level.go:

package model


// Immortal level
type Level struct {
	level      string
	levelValue int
}

func NewLevel(level string, levelValue int) Level {
	return Level{
		level:      level,
		levelValue: levelValue,
	}
}


// Get the level description
func (recv Level) Level(a) string{
	return recv.level
}

func (recv *Level) SetLevel(level string) {
	recv.level = level
}

func (recv *Level) LevelName(a) string{
	return recv.level
}


Copy the code

src/go_code/method/model/lingen.go:

package model

// The spiritual root of a monk
type lingGen struct {
	linGenNames[] string
}

func NewLinggen(name ...string) *lingGen {
	return &lingGen{linGenNames: name}
}

func (recv *lingGen) LingGenNames(a) []string {
	return recv.linGenNames
}

Copy the code

Import and use in the main package

package main

import (
	"fmt"
	"go_code/method/model"
)

func main(a) {

	im := model.NewImmortal2(18.Mr.han ""."Male"."Nine layers of qi training".9200."Wood Root"."Water root"."Fire Spirit Root"."Earth spirit root")
	im.SetLevel("Great Perfection of qi training")
	fmt.Println("Realm:",im.LevelName())
	fmt.Println("Lingen:",im.LingGenNames())
}

Copy the code

Output:

Realm: Practicing Qi and perfecting Spirit Root: [Mu Ling root, Shui Ling root, Fire Ling Root, Earth Ling root]Copy the code

Go’s types and methods compare to other object-oriented languages

In object-oriented languages such as C++, Java, C#, and Python, methods are defined and inherited in the context of a class: when a method is called on an object, the runtime checks whether the class and its superclass have a definition for the method, and if it does not, an exception occurs.

In Golang, such an inheritance hierarchy is completely unnecessary: if a method is defined on this type, it can be called, regardless of whether it exists on any other type. In this sense, Golang is more flexible.

Golang does not require an explicit class definition, as Java and C++ do, etc. Instead, a “class” is implicitly defined by providing a set of methods that operate on a common type plus the type itself. The type can be a structure or any user-defined type.

conclusion

In Golang, class = type + set of methods associated with it.

In Golang, code reuse is achieved through composition and delegation, and polymorphism is achieved through the use of interfaces: sometimes called Component Programming.

Go’s interface (more on that later) provides a much more powerful but simpler polymorphic behavior than class inheritance.

Write in the back

So that’s it for Golang. The examples covered in this article can be downloaded here. If my study notes can help you, please give me a thumbs up and encouragement. If there are mistakes and omissions in the article, please help to correct them.