This is the 28th day of my participation in the August Text Challenge.More challenges in August
Day 1: Basics of go objects. How to create struct, method, constructor (factory function), receiver pattern
Day 2: Packages, how to introduce external packages and system packages (defining aliases or combinations)
Day 3: Define a main method for each directory.
Introduction to Object orientation
- The GO language only supports encapsulation, not inheritance and polymorphism.
So what inheritance and polymorphism do, how do we do that? Using interfaces, go is interface oriented programming.
- Go only supports encapsulation, so go has no classes, only structs
The usage of the structure
- Struct creation method
type TreeNode struct { Value int Left, Right *TreeNode} func main() {var root TreeNode root = TreeNode{Value:4} root.Left = &treenode {} root.right = &TreeNode{5, nil, nil} root.Left.Right = new(TreeNode) fmt.Println(root) }Copy the code
-
Several ways to create an instance
- var root TreeNode
- TreeNode{}
- Use the built-in function new
-
Use both the address and the structure itself. To access members
- This sentence is very important, before I have not understood, why the structure is also hit can access it
- Method of instantiating a structure in slice
Func main() {var root TreeNode root = TreeNode{Value:4} root.Left = &treenode {} root.Right = &treenode {5, nil, nil} root.Left.Right = new(TreeNode) fmt.Println(root) nodes : = []TreeNode{ {4, nil,nil}, {}, {Value:3}, {5, nil, &root}, } fmt.Println(nodes) }Copy the code
When constructing a structure in Slice, you can omit the structure name
nodes := []TreeNode{
{4, nil,nil},
{},
{Value:3},
{5, nil, &root},
}
Copy the code
3. Go language constructor?
root = TreeNode{Value:4}
root.Left = &TreeNode{}
root.Right = &TreeNode{5, nil, nil}
root.Left.Right = new(TreeNode)
Copy the code
- The GO language has no constructor. But as you can see from the example above, he has given a variety of constructors, no arguments, one argument, multiple arguments
- What if we still want to define our own constructor? We can process the function.
type TreeNode struct {
Value int
Left, Right *TreeNode
}
func NewTreeNode(value int) *TreeNode {
return &TreeNode{Value:value}
}
Copy the code
- If you look at the constructor, the input parameter is a value and the output parameter is the address of a TreeNode. The return value is new a local variable. That’s the factory function.
- The factory function returns an address
Problem: NewTreeNode returns the address of a local variable. This is not allowed in Java. But it is allowed in GO.
Is the local TreeNode in the heap or on the stack?
C language, local variables are placed on the stack, if you want to be accessed by others must be placed on the heap, after the completion of manual recycling.
Java language, classes are placed on the heap, when the use of a new, will be automatically garbage collection
With GO, we don’t need to know if it’s created on the heap or on the stack. This is determined by the go language’s compiler and runtime environment. He’s going to decide, if the TreeNode doesn’t take the address, it doesn’t need to be used by anyone else, it’s going to be allocated on the stack, and if the TreeNode returns the address, it’s going to be used by someone else, it’s going to be allocated on the heap. Once it’s allocated on the heap, it’s recycled
As above: we define a structure like this
type TreeNode struct { Value int Left, Right *TreeNode} func NewTreeNode(value int) *TreeNode {return &TreeNode{value :value}} func main() {// create structure method var root TreeNode root = TreeNode{Value:3} root.Left = &TreeNode{} root.Right = &TreeNode{5, nil, nil} root.Left.Left = new(TreeNode) root.Left.Right = NewTreeNode(2) }Copy the code
4. How to define a method for a structure
type TreeNode struct { Value int Left, Right *TreeNode } func NewTreeNode(value int) *TreeNode { return &TreeNode{Value:value} } func (node TreeNode) Print() { fmt.Println(node.Value) }Copy the code
We defined a Print method,
-
There is a receiver (node TreeNode), equivalent to this in other languages. In fact, the go language defines methods in the same way as normal method definitions
func Print(node TreeNode) { fmt.Println(node.Value) } Copy the code
The function is the same, but it’s the method of the structure that’s written up front. There are slight differences in use
Print (root); print(root)Copy the code
Question: Since node TreeNode is written like a normal function, does it pass a value or a reference? The answer is pass. Let’s verify that
type TreeNode struct { Value int Left, Right *TreeNode } func NewTreeNode(value int) *TreeNode { return &TreeNode{Value:value} } func (node TreeNode) Print() { Println(node.value)} func (node TreeNode) setValue() {node.value = 200} func main() {var root TreeNode root = TreeNode{Value:3} root.Left = &TreeNode{} root.Right = &TreeNode{5, nil, nil} root.Left.Left = new(TreeNode) root.Left.Right = NewTreeNode(2) root.Print() root.setValue() root.Print() }Copy the code
Output result:
33 Copy the code
The setValue() method has changed its Value to 200, but it still prints 3. Receiver method method definition is the value of the way copy, internal modification, does not affect the outside
So, how do we get him to set, we send him an address
func (node *TreeNode) setValue() { node.Value = 200 } Copy the code
The difference with the previous method is that the receiver passes an address. The usage is the same as before. This enables address copying, internal modification, and external validity.
Conclusion:
1. The print() method is called to print a copy of the value
2. Call setValue() to copy the address and assign values to the object in the address.
4. Nil Pointers can also call methods
Note: The emphasis here is on nil Pointers. Not a nil object
Why do you write it out here? It’s because he’s different from what I’ve learned in Java: null objects call methods, property calls give an error, whereas nil can call methods.
So let’s look at this demo
type TreeNode struct { Value int Left, Right *TreeNode } func NewTreeNode(value int) *TreeNode { return &TreeNode{Value:value} } func (node TreeNode) Print() { fmt.Println(node.Value) } func (node *TreeNode) setValue() { node.Value = 200 } func main() { var node TreeNode fmt .Println(node) node.Print() node.setValue() node.Print() }Copy the code
Output result:
{0 <nil> <nil>}
0
200
Copy the code
Here the treeNode in Main is an object, not an address. It will give a default value if there is no value at initialization. So, using it to call, it’s definitely okay. We’re talking about null Pointers here. Let’s look at the null pointer case
type TreeNode struct { Value int Left, Right *TreeNode} func (node *TreeNode) Print() {if node == nil {ftt.println ("node is null ") return} fmt.Println(node.Value) } func main() { var node *TreeNode fmt.Println(node) node.Print() }Copy the code
The difference here is that the TreeNode is a pointer.
Let’s see what happens
A <nil> node is a null pointerCopy the code
Indeed, the Print method is successfully called and the Node object is caught as empty
But it’s important to note that calling a property on a nil object is still an error.
type TreeNode struct { Value int Left, Right *TreeNode} func (node *TreeNode) Print() {if node == nil {FMT.Println("node is null ") // return} fmt.Println(node.Value) } func main() { var node *TreeNode fmt.Println(node) node.Print() }Copy the code
Comment out return. Look at the results
A panic exception was reported.
So does the pointer receiver come up and say is this pointer nil? It doesn’t have to be. It depends on the usage scenario.
- Traversal of structure functions
func(node *TreeNode) traveres() {
if node == nil{
return
}
node.Left.traveres()
node.Print()
node.Right.traveres()
}
Copy the code
Iterate over the left subtree, print it out, iterate over the second subtree, print it out
Results:
0, 0, 3, 5, 4Copy the code
Note how node.left.traveres () is written here. We only check if node is nil. In Java, we also check if Node. Left is null. Otherwise it throws an exception, but go doesn’t, and a nil pointer can call a method
Should I use a value receiver or a pointer receiver?
-
To change the content, you must use the pointer receiver
-
Using pointer receivers is also considered for large structures: large structures consume a lot of memory
-
Consistency: If pointer receivers are available, it is best to use pointer receivers (recommended)
-
Value receivers are specific to the GO language. Pointer receivers are also found in other languages, c has a pointer to this, Java’s this is not a pointer, it is a reference to an object, python has self.
-
The value/pointer receiver can receive the value/pointer: this means that I can call Print on either an object or a pointer object
Func main() {var root TreeNode root = TreeNode{Value:3} root.Left = &treenode {} root.Right = &treenode {5, nil, nil} root.Left.Left = new(TreeNode) root.Right.Right = NewTreeNode(4) root.traveres() var node * TreeNode node.traveres() }Copy the code
Root is a value, node is a pointer, both can call pointer receiver traveres. Similarly, root and Node can both invoke a value receiver
Three.
The main point in the bag is
-
The uppercase letter stands for public and the lowercase letter stands for private
-
Package definition: a directory can have only one package. For example, if you define a folder named tree. Then all files in it will be named tree. Or both main(which is also allowed). You can’t have both tree and main.
How to extend system packages or packages defined by others?
Suppose there’s a structure written by someone else that I want to use, but it doesn’t meet my needs, and I want to extend it, how do I extend it?
In other languages, such as c++ and Java, inheritance is inherited, but there are many disadvantages to inheritance. So go cancels inheritance. This is implemented in two ways
- Define an alias
- Using the combination
- Define aliases: such as the treeNode example above. If I want to extend in another package, how do I do that by defining an alias?
Package main import ("aaa/tree" "FMT") Now you want to extend a back-order traversal. What to do? The first step is to define a type and then refer to an external type. Use a pointer when referencing a structure, or else make a value copy of the original structure. Expand your method type myTreeNode struct {node * tree. TreeNode} func (myNode * myTreeNode) postorder () {if myNode = = nil | | myNode.node == nil{ return } left := myTreeNode{myNode.node.Left} left.postorder() right := MyTreeNode {mynode.node.right} right.postorder() fmt.print (mynode.node.value)} func main(){var root tree.TreeNode root = tree.TreeNode{Value:3} root.Left = &tree.TreeNode{} root.Right = &tree.TreeNode{5, nil, nil} root.Left.Left = new(tree.TreeNode) root.Right.Right = tree.NewTreeNode(4) root.Traveres() var node *tree.TreeNode node.Traveres() treeNode := myTreeNode{&root} treeNode.postorder() }Copy the code
Step 1: Define a type of your own, and then introduce an external structure. This grouping introduces pointer types, otherwise a value copy of the external structure must be made
type myTreeNode struct {
node *tree.TreeNode
}
Copy the code
By doing so, the object now has the TreeNode structure that was originally defined. Imagine using it and passing in a structure of type TreeNode. We then operate on the TreeNode structure
Step 2: implement your own method, sequential traversal
Func (myNode * myTreeNode) postorder () {/ / to note here is that the myNode node may be empty nodes. If myNode = = nil | |. MyNode node = = nil {return } left := myTreeNode{myNode.node.Left} left.postorder() right := myTreeNode{myNode.node.Right} right.postorder() fmt.Print(myNode.node.Value) }Copy the code
Gets the external structure, and then gets the left subtree of the structure. After obtaining the right subtree of the structure, print it out, and then realize the call to the original structure.
Step 3: Invoke
Func main(){var root tree.treenode root = tree.treenode {Value:3} root.left = &tree.treenode {} root.right = &tree.TreeNode{5, nil, nil} root.Left.Left = new(tree.TreeNode) root.Right.Right = tree.NewTreeNode(4) root.Traveres() var node *tree.TreeNode node.Traveres() treeNode : = myTreeNode{& root} treeNode.postorder() }Copy the code
The call is simple: root passes in the address and calls the method
- An alias is defined to implement calls to external or system constructs
Now let’s define an alias for the slice
package main import "fmt" type Queue []int func(q *Queue) add(v int){ *q = append(*q, v) } func(q *Queue) pop() int{ tail := (*q)[len(*q)-1] *q = (*q)[:len(*q)-1] return tail } func(q *Queue) isEmpty() bool { return len(*q) == 0 } func main() { q := Queue{1} q.add(2) q.add(3) fmt.Println(q.pop()) fmt.Println(q.pop()) fmt.Println(q.isEmpty()) fmt.Println(q.pop()) fmt.Println(q.isEmpty()) }Copy the code
Step 1: Define an alias for the slice
type Queue []int
Copy the code
Then operate on the slice and add an element
func(q *Queue) add(v int){
*q = append(*q, v)
}
Copy the code
Note here: in the add method. We said the receiver is written like this, but in this method, *q modifies the address. So after add, it’s not the same address anymore.
It’s not going to be the same address
Func main () {q: Queue = {1} FMT) Printf (" address: 0 x % x \ n ", & q [0]) q.a dd FMT. (2) Printf (" address: 0x%x \n", &q[1]) q.add(3) fmt.Println(q.pop()) fmt.Println(q.pop()) fmt.Println(q.isEmpty()) fmt.Println(q.pop()) fmt.Println(q.isEmpty()) }Copy the code
Address: 0xC000096008 Address: 0xC000096028 3 2 False 1 TrueCopy the code
The two printed addresses are different. Which means his address has changed
5. Definition of package name, each folder can only have one main
Let’s use the system package as an example
So, when we define files, we define a main function under each folder