Golang 面向“对象”

结构体和方法
package main

import "fmt"

// 定义结构体,二叉树结点
type treeNode struct {
	value       int
	left, right *treeNode
}

// 工厂函数
func createNode1(value int) treeNode {
	return treeNode{value: value}
}

// 也可以返回指针
func createNode2(value int) *treeNode {
	return &treeNode{value: value} // 虽然返回的是 局部变量 地址,但也 ok
}

func printNode(node treeNode) {
	fmt.Println(node.value)
}

func (node treeNode) printNode2() {
	fmt.Println(node.value)
}


func (node *treeNode) setValue(value int) {
	node.value = value
}

func main() {
	var node treeNode
	fmt.Println(node) // {0 <nil> <nil>}
	// 赋值操作
	node = treeNode{value: 3}
	fmt.Println(node) // {3 <nil> <nil>}
	node.left = &treeNode{}
	node.right = &treeNode{5, nil, nil}
	fmt.Println(node) // {3 0xc0000044e0 0xc000004500}

	// 还可以这样
	node.left.right = new(treeNode) // left 是结构, right 是地址,都用 “.” 来访问

	// 创建结构体 切片
	nodes := []treeNode{
		{0, nil, nil}, // 切片里可以省略 treeNode 定义
		{value: 1},
		{left: nil},
	}
	fmt.Println(nodes)

	// 没有构造方法,但是可以自己写 工厂函数
	node1 := createNode1(45)
	node2 := createNode2(45) // 得到指针,*node2 对应的是 createNode2 内的局部变量
	// java和python: 变量都是分配在堆上的,由垃圾回收机制回收, go语言会视情况决定将局部变量分配在栈上还是堆上
	// 若需要返回给函数外部使用,则会分配在堆上, 反之分配在栈上(函数结束后直接销毁)
	fmt.Println(node1, *node2)

	// 给结构体绑定方法
	// 首先看这个函数:
	printNode(node) // 这样会打印 出value 3

	// 可以换一种方法定义, 如 printNode2
	// 相当于给结构体 treeNode 绑定了一个方法
	node.printNode2()
	node.setValue(666) // 即使这个函数定义的 node 是指针,但仍只用 . 就行了,编译器会判断然后自己传地址过去
	node.printNode2()

}

封装

        Go语言没有类的概念,因此也没有继承和多态,只有封装
        包:package  main,  这个表示一个叫  main  的包,在main  包下才可以定义  main  函数
        调用其他包的例子:
                utils目录下建议一个mapHelper.go  文件

package utils  // 和目录名一致

import (
	"fmt"
	"sort"
)

// 按序遍历 map
func TraverseMapInorder(m map[string]string) {
	keys := []string{}
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys{
		fmt.Print(k, ": ", m[k], ", ")
	}
}
使用首字母大小写区分 public(首字母大写) 和 private(首字母小写),
不止函数,还有变量,结构,结构内的变量,都应用此规则。

                在其它地方调用:

package main

import "learning/utils"

func main() {
	// 每个目录只能有一个包
	m := map[string]string{
		"name": "yujun",
		"age": "26",
	}

	utils.TraverseMapInorder(m) // age: 26, name: yujun,
	// 使用首字母大小写区分 public(首字母大写) 和 private(首字母小写)
	// 为结构定义的方法必须放在同一个包内, 但可以是不同的文件
}
扩展已有类型
package main

import "fmt"

type oriNode struct {
	value       int
	left, right *oriNode
}

type myNode struct {
	node *oriNode
}

func (node *oriNode) oriMethod() {
	fmt.Println("这是oriNode的原生方法")
}

func (node *myNode) extendMethod() {
	fmt.Println("这是扩展的方法, 打印value:", node.node.value)
}

// 定义了一个数据结构,本质其实就是 oriNode, 只是起了个别名
type myNode2 oriNode

// 给 myNode2 绑定方法
func (node *myNode2) extendMethod2() {
	fmt.Println("这是通过别名扩展绑定的方法,打印value:", node.value)
}

// 内嵌方式
type myNode3 struct {
	*oriNode
}

// 给 myNode3 绑定方法
func (node *myNode3) extendMethod3() {
	fmt.Println("这是通过内嵌扩展绑定的方法,打印value:", node.value)
}

func main() {
	// 方式一:通过组合, 扩展结构体 oriNode,让它拥有新的方法, 这是常用的方式
	node := oriNode{value: 3}
	n := myNode{node: &node} // 定义一个包含oriNode 的新的结构体
	n.extendMethod()

	// 方式二:通过别名
	// 实际上是自定义一个数据结构,如 myNode2
	node2 := myNode2{value: 34}
	node2.extendMethod2() // 很显然,别名的方式,使用起来要方便的多,但扩展性较差

	// 方式三:通过内嵌 (Embedding)
	// 如 myNode3, 需要生成一个 oriNode, 然后再用 myNode3 包起来
	node3 := myNode3{&node}
	node3.extendMethod3() // 方法调用和使用别名一样
	node3.oriMethod()     // 还可以直接使用内嵌类型的方法, 类似其他语言的 继承

}