结构体和方法
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() // 还可以直接使用内嵌类型的方法, 类似其他语言的 继承
}