preface
Begin to learn Chapter 10 structure and method. This is the learning notes of the Go getting started guide.
Structure definition
type identifier struct { field1 type1 field2 type2 ... }
package main import "fmt" type point struct { x float64 y float64 } func main() { var p1 point p1.x = 5 p1.y = 5 fmt.Println(p1) fmt.Println(p1.x) fmt.Println(p1.y) }
Use the new function to allocate memory to a new structure variable, which returns a pointer to the allocated memory:
package main import "fmt" type point struct { x float64 y float64 } func main() { var p1 *point p1 = new(point) p1.x = 1 p1.y = 1 fmt.Println(p1)//&{1 1} p2 := new(point) p2.x = 2 p2.y = 2 fmt.Println(p2)//&{2 2} }
Declaring var t T will also allocate memory to T and zero the memory, but t is type T at this time. In both ways, t is often referred to as an instance or object of type T.
Initialize structure instance:
package main import "fmt" type point struct { x float64 y float64 } func main() { p1 := &point{1,2} //Must be written in field order fmt.Println(p1) p2 := &point{y:1,x:2}//With field name fmt.Println(p2) }
The expressions new(Type) and & type {} are equivalent.
It is possible that some fields in an exported structure type are exported and others are not.
Structure factory
The Go language does not support the constructor method in the object-oriented programming language, but the "constructor factory" method can be easily implemented in Go, usually starting with new or new:
package main import "fmt" type Point struct { x float64 y float64 } func NewPoint(x float64, y float64) *Point { return &Point{x,y} } func main() { p1 := NewPoint(1,2) fmt.Println(p1) }
It is similar to new Point(...) in object-oriented, But it's easier.
Moreover, if the initial letter of the structure is lowercase (not visible outside the package), you can only use the factory method if you want to use the structure outside the package.
Label of structure
In addition to the name and type, the field in the structure can also have an optional tag: it is a string attached to the field, which can be a document or other important marks:
type Point struct { x float64 "this is x" y float64 "this is y" }
You need to get through reflection. You will learn in the next chapter:
package main import ( "fmt" "reflect" ) type Point struct { x float64 "this is x" y float64 "this is y" } func NewPoint(x float64, y float64) *Point { return &Point{x,y} } func main() { tt := NewPoint(1,5) for i := 0; i < 2; i++ { refTag(tt, i) } } func refTag(tt *Point, ix int) { ttType := reflect.TypeOf(tt) if ttType.Kind() == reflect.Ptr { ttType = ttType.Elem() } ixField := ttType.Field(ix) fmt.Printf("%v\n", ixField.Tag) } //this is x //this is y
Anonymous field
Even the field in the structure can have no explicit name, so its type is its name:
package main import "fmt" type Person struct { int string } func main() { person := new(Person) person.int = 5 person.string = "ego" fmt.Println(person) }
In a structure, there can only be one anonymous field for each data type.
Embedded structure
The anonymous field itself can also be a structure, so it is called an embedded structure. For example:
package main import "fmt" type Thing struct { thing1 string thing2 string } type Person struct { age int name string Thing } func main() { person1 := new(Person) person1.age = 5 person1.name = "ego" person1.thing1 = "aaa" person1.thing2 = "bbb" fmt.Println(person1)//&{5 ego {aaa bbb}} person2 := Person{1,"ego",Thing{"abc","def"}} fmt.Println(person2)//{1 ego {abc def}} }
You can directly use person1 Thing1 to access the embedded structure, or through person1 Thing. Thing1 to access.
This embedded structure can be used to simulate inheritance like behavior. Inheritance in Go language is realized through embedding or combination, so it can be said that in Go language, combination is more popular than inheritance.
name conflict
- The outer name overwrites the inner name (but the memory space of both is reserved), which provides a way to overload fields or methods;
- If the same name appears twice at the same level, if the name is used by the program, it will cause an error (it doesn't matter if it is not used). There is no way to solve the ambiguity caused by this problem, which must be corrected by the programmer himself.
Let's talk about rule one:
package main import "fmt" type A struct { a int B } type B struct { a string } func main() { ego := A{1,B{"a"}} fmt.Println(ego.a) fmt.Println(ego.B.a) }
The field a has the same name, but is not in the same layer, so the a int of the outer layer will overwrite the a string of the inner layer, so access ego A gets the outer layer. But visit ego B. A will get the inner a.
method
What is the method
Considering that there is no concept of class in go, some association is needed.
Structure can be regarded as a simplification of class, and the method is the function on the receiver. The receiver can be almost any type, but it cannot be an interface type. It cannot be a pointer type, but it can be a pointer of any other allowed type.
In Go, the type code and the method code bound to it can not be placed together. They can exist in different source files. The only requirement is that they must be in the same package.
An alias type cannot have a method already defined on its original type.
The format of the method is as follows:
func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }
Compared with functions, recv receiver_type is added between func and methodName to indicate the receiver and receiver type.
An example:
package main import "fmt" type Person struct { name string age int } type Employee struct { salary float64 Person } func (e *Employee) giveRaise(percent float64) { e.salary += e.salary * percent } func main() { e := new(Employee) e.name = "ego" e.age = 18 e.salary = 10000 e.giveRaise(0.1) fmt.Println(e) }
As for whether the receiver's type is a pointer or not, we will find that if it is e Employee, e.giveRaise() can also be called because the compiler has implicitly converted it for us. On the contrary, the same is true. But there will be problems with the interface type. I'll talk about it later.
Articles involved: https://zhuanlan.zhihu.com/p/76384820
Both pointer and value methods can be called on a pointer or non pointer
Methods are not mixed with data definitions (structures): they are orthogonal types; representation (data) and behavior (Methods) are independent.
Methods and fields not exported
Similar to the private field in Java, you need to use getter and setter methods to access and modify it. In the Go language, the getter method only uses the member name, and the setter method uses the Set prefix:
package main import "fmt" type Person struct { name string age int } type Employee struct { salary float64 Person } func (person *Person) Name() string { return person.name } func (person *Person) SetName(name string) { person.name = name } func (person *Person) Age() int{ return person.age } func (person *Person) SetAge(age int) { person.age = age } func (e *Employee) Salary() float64{ return e.salary } func (e *Employee) SetSalary(salary float64) { e.salary = salary } func (e *Employee) giveRaise(percent float64) { e.salary += e.salary * percent } func main() { e := new(Employee) e.name = "ego" e.age = 18 e.salary = 10000 e.SetSalary(999) fmt.Println(e) }
The fields (properties) of an object should not be changed by two or more different threads at the same time
Methods and inheritance of embedded types
When an anonymous type is embedded in the structure, the visible methods of the anonymous type are also embedded, which is equivalent to the effect that the outer type inherits these methods: putting the parent type in the subtype to implement the subtype.
package main import "fmt" type Person struct { name string age int } type Employee struct { salary float64 Person } func (person *Person) SayHello() { fmt.Println("hello,"+person.name) } func main() { e := new(Employee) e.name = "ego" e.age = 18 e.salary = 10000 e.SayHello() }
A method of an outer type with the same name as an embedded type method overrides the method corresponding to the embedded type:
package main import "fmt" type Person struct { name string age int } type Employee struct { salary float64 Person } func (person *Person) SayHello() { fmt.Println("hello,"+person.name) } func (e *Employee) SayHello() { fmt.Println("hello,hello,"+e.name) } func main() { e := new(Employee) e.name = "ego" e.age = 18 e.salary = 10000 e.SayHello() }
When a structure is embedded in the same package as itself, you can access all fields and methods of each other.
In go, types are classes (data and associated methods). Go has the concept of class inheritance similar to object-oriented language. Inheritance has two benefits: code reuse and polymorphism.
In Go, code reuse is realized through composition and delegation, and polymorphism is realized through the use of interfaces: sometimes this is also called Component Programming.
The String() method of type is actually similar to the ` ` toString() 'method of class in Java.
Garbage collection and SetFinalizer
Go developers do not need to write code to release the memory occupied by variables and structures that are no longer used in the program. In the go runtime, there is an independent process, the garbage collector (GC), which will handle these things. It searches for variables that are no longer used and then releases their memory. The GC process can be accessed through the runtime package.
By calling runtime The GC () function can explicitly trigger GC.
If you need to perform some special operations before an object obj is removed from memory, such as writing to a log file, you can call the function in the following ways:
runtime.SetFinalizer(obj, func(obj *typeObj))