Chapter 6 Methods
- A method is a function associated with a particular type
- To the two key points of OOP programming, encapsulation and composition
Common libraries and methods
time.Duration
time.Hour
time.Duration.Seconds()
6.1 Method declaration
focus
- When a function is declared, put a variable before its name, which is a method
- The receiver of golang is not like
this
the sum of other languagesself
, which can be chosen arbitrarily. In order to keep the shortness of the new one passed between methods, it is recommended to use the first letter of the first letter of the type in lowercase. p.Distance
The expression is called a selector, and the selector will also be used to select a field of type struct- Since the methods and fields are in the same namespace, the names of the methods and fields should not be the same, otherwise the compilation will not pass
- Different types have different namespaces, which means that different types can have methods with the same name
- Unlike other OOP languages, Go language can specify methods for any type, so it is convenient to define some methods for simple values, strings, slices, and maps in Go language. Methods can be declared on any type, as long as it is not a pointer or an interface
Common libraries and methods
math.Hypot
6.2 Pointer-based methods
focus
- The name of the method is
(*Point).ScaleBy
. The parentheses here are required; without them the expression might be interpreted as*(Point.ScaleBy)
. - Only types (Point) and pointers to them (*Point) are the two types of receivers that may appear in receiver declarations
- In order to avoid ambiguity, when declaring a method, if a type name itself is a pointer, it is not allowed to appear in the receiver, such as
type P *int func (P) f() { /* ... */ } // compile error: invalid receiver >type
- If the receiver needs a pointer as the receiver, the Go language can directly use the type variable + selector to call the method, because the Go language compiler will implicitly use the variable to
&
call this method, also for the receiver of the type variable, the pointer variable It can be used directly when calling, and the compiler will add it*
. for example:
方法(类型变量直接作为指针接收器调用): func (p *Point) ScaleBy(factor float64) ---- p := Point{1, 2} p.ScaleBy(2) //是允许的,编译器会使用&p去调用 ---- 方法(类型指针直接作为类型变量接收器调用): func (p Point) Distance(q Point) float64 ---- p := Point{1, 2} pptr := &p pptr.Distance(q) //是允许的,编译器会使用*pptr去调用
This way of writing is only applicable to "variables" - elements in fields, arrays, and slices, butcannot call a pointer method through a receiver whose address cannot be taken, for example, the memory address of the temporary variable cannot be obtained, and the following call is wrong:
Point{1, 2}.ScaleBy(2) // compile error: can't take address of >Point literal --------下面的这个是可以的,因为Distance()的接收器是类型变量 Point{1, 2}.Distance(q) // it's ok
- When the receiver is a type variable, the method operates on a copy of the object; when the receiver is a pointer, the method operates on a reference to the object, and changes to members will be applied to the object.
- Summarize:
- Regardless of whether the receiver of your method is a pointer type or a non-pointer type, it can be called through a pointer/non-pointer type, and the compiler will help you do type conversion.
- When declaring whether the receiver of a method should be a pointer or a non-pointer type, you need to consider two aspects. The first aspect is whether the object itself is particularly large. If it is declared as a non-pointer variable, the call will generate a copy; the second The second aspect is that if you use a pointer type as a receiver, then you must pay attention that this pointer type always points to a memory address, even if you copy it. Those familiar with C or C++ should be able to understand it quickly here.
- Array, slice, map These types are used as receivers, whether they are variables or pointers, they all represent a pointer
6.2.1 Nil is also a legal receiver type
focus
- A method can theoretically also use a nil pointer as its receiver, especially when nil is a legal zero value for an object, such as a map or slice
6.3 Extending types by embedding structs
focus
- Structure
S
Embedding The structureT
can be an anonymous field or a named field. AssumingT
there are membersM
, the method of the named field cannot beS.M
called directly, but can only beS.Tname.M
called. - The embedded structure can be understood as a kind of inheritance, but it is somewhat different from inheritance, namely: inheritance in other languagesis "is a", and the embedded structure of Go languageis "has a". That is to say, the new type cannot replace the embedded type and be directly passed to the method that requires the embedded type parameter, and the embedded type must be explicitly selected. for example:
var p = ColoredPoint{Point{1, 1}, red} var q = ColoredPoint{Point{5, 4}, blue} p.Distance(q) // compile error: cannot use q (ColoredPoint) as >Point p.Distance(q.Point) // compile success.
- Given the following example, all the members and methods owned can be
ColoredPoint
obtained through or directly :Point
ColoredPoint.Point.X
ColoredPoint.X
X
type Point struct{ X, Y float64 } type ColoredPoint struct { Point Color color.RGBA }
- Embedded anonymous fields may also be pointers to a named type, in which case fields and methods are indirectly introduced into the current type, allowing us to share common structures and dynamically change relationships between objects
- A struct type may also have multiple anonymous fields
- When the compiler resolves a selector to a method, it will first look for the methods directly defined in this
type , then find the methods introduced by the embedded fields, then find the methods introduced by the embedded fields of the embedded fields, and then Keep looking down recursively. If the selector is ambiguous, the compiler will report an error. For example, if you have two methods with the same name at the same level, you need to explicitly specify the anonymous field until there is no ambiguity. - In the book, the difference between the function and method defined by the author refers to whether there is a receiver, instead of referring to whether there is a return value as in other languages.
- When
T
it is a type, the method expression may be writtenT.f
or(*T).f
, will return a function "value", this function will use its first parameter as a receiver, soPoint.Distance
it can be used directly as a function, cross-package calls also need plus package namepackage.Point.Distance
6.4 Method values and method expressions
focus
- Method Value - "Method Value"
distanceFromP := p.Distance // method value distanceFromPValue := p.Distance() // method result
6.5 Example: Bit Array
Key
common libraries and methods
bytes.Buffer
bytes.Buffer.WriteByte
6.6 Packaging
focus
- An object's variables or methods are generally defined as "encapsulated" if they are not visible to the caller
- The Go language has only one means of controlling visibility: uppercase identifiers are exported from the package in which they are defined, lowercase identifiers are not. The same applies to struct or a method of type
- In Go language, if we want to encapsulate an object, we must define it as a struct.
- This name-based approach makes the smallest unit of encapsulation in the language a package, rather than a type like in other languages.
- A field of type struct is visible to all code in the same package, whether your code is written in a function or a method.
- Encapsulation offers three advantages.
- First, because the caller cannot directly modify the variable values of the object, it only needs to pay attention to a small number of statements and understand the possible values of a small number of variables.
- Second, hiding the details of the implementation can prevent the caller from relying on the specific implementations that may change, so that the programmers who design the package can get greater freedom without breaking the external API.
- Third, it prevents the external caller from arbitrarily modifying the value inside the object.
- Functions that only access or modify internal variables are called setters or getters