go语言仅支持封装,不支持继承和多态。
go更没有c++的虚函数啥的。
所以go只有struct,没有class,这个倒是有点像c。
type TreeNode struct {
Left, Right *TreeNode
Value int
}
func main() {
node := TreeNode{Value : 3} // 这个只赋值了value
node.Right = &TreeNode{} // 这是建了一个空结点
node.Left = &TreeNode{nil, nil, 5} // 可以省略字段,直接按顺序赋值
node.Right.Left = new(TreeNode) // 当然也是可以使用new的
nodes := []TreeNode { // 定义一个结构体切片
{Value : 3},
{},
{nil, &node, 6},
}
fmt.Println(nodes)
}
go语言没有构造函数,这个感觉是因为go只支持struct的,不支持类,struct只又变量,所以变量可以赋初值就可以了。
当然如果想自己写构造函数,那就跟c语言一样,写一个工厂函数,返回一个类对象。
func creatNode(value int) *TreeNode {
return &TreeNode{Value : value}
}
这种玩法,让我想起了当初用c语言来搞面向对象,就是这种感觉。
注意:如果按c++的标准,这个是返回了局部变量,c++的局部遍历是存储在栈上的,返回指针是有问题的,但是go语言不存在这个问题,因为go语言根据程序员的写法,自动推断这个变量是存储在栈上还是堆上。
接下来我们看看go的struct的方法怎么定义:
func (node TreeNode) println() {
fmt.Println(node)
}
把类型定义在前面,这个在go语言中的说法是接收者,也就是我调用的时候:
node.println() // node对象会传给这个println函数,printfln函数会接收这个node对象
这种方式是值接收者,是不可以修改对象中的值的,只有指针接收者才能修改变量的值:
func (node *TreeNode) setValue(value int) {
node.Value = value
}
看着其实问题不大,就改成一个指针。
其他语言的接收者,也就是this指针,其实都是传指针,只有go语言支持两个。
注意:go语言nil是可以调用的,只不过是取不到值的
var pRoot *TreeNode
pRoot.setValue(33) // 这是可以执行的,调用到函数内部的,需要在函数内部判断
func (node *TreeNode) setValue(value int) {
if node == nil { // 这里加判断就可以了
return
}
node.Value = value
}
如果是c++语言的话,需要在指针调用的时候,就需要判断了。
要改变内容必须使用指针接收者
结构过大也考虑使用指针接收者
一致性:如有指针接收者,最好都是指针接收者
名字一般使用CamelCase(驼峰法)
首字母大写:public (公有方法或变量)
首字母小写:private (私有方法或变量)
每个目录一个包(包名和目录名可以不一样,但是一个目录只能有一个包)
main包包含可执行入口(其他包可以写main函数么,可以试试)
为结构定义的方法必须放在同一个包内(可以多文件,但一定要在一个包内)
package main
import (
"./node" // 包的引入,是引入文件夹,这个要记住
)
func main() { // main包的mian函数才是执行的开始
node := tree.TreeNode{Value : 3} // 这个只赋值了value
node.Right = &tree.TreeNode{} // 这是建了一个空结点
node.Left = &tree.TreeNode{nil, nil, 5} // 可以省略字段,直接按顺序赋值
node.Right.Left = new(tree.TreeNode) // 当然也是可以使用new的
node.Right.Right = tree.CreatNode(2)
node.Println()
node.SetValue(66)
node.Println()
var pRoot *tree.TreeNode
pRoot.SetValue(33)
}
这是tree包
package tree
import (
"fmt"
)
func CreatNode(value int) *TreeNode {
return &TreeNode{Value : value}
}
type TreeNode struct {
Left, Right *TreeNode
Value int
}
func (node TreeNode) Println() {
fmt.Println(node)
}
func (node *TreeNode) SetValue(value int) {
if node == nil {
return
}
node.Value = value
}
func main() { // 确实可以在其他包写main函数,就相当是一个测试用例,但是不知道怎么编译的,好像没有mian包编译不了,可以先写成main包,然后在修改成单独的包
node := TreeNode{Value : 3} // 这个只赋值了value
node.Right = &TreeNode{} // 这是建了一个空结点
node.Left = &TreeNode{nil, nil, 5} // 可以省略字段,直接按顺序赋值
node.Right.Left = new(TreeNode) // 当然也是可以使用new的
node.Right.Right = CreatNode(2)
node.Println()
node.SetValue(66)
node.Println()
var pRoot *TreeNode
pRoot.SetValue(33)
}
总结两点:包引入记得是文件夹名,包的文件名可以跟包名不一样。
如果是其他语言,扩展别人类型,是可以通过继承的方式来扩展。
因为go语言没有继承,所以我们来看看go语言是怎么扩充别人的类型。
定义别名
使用别名,就难了一点点
// 使用别名
type queue []int // 用type来定义[]int别名
func (q *queue) push(value int) { // 封装好了就各种操作
*q = append(*q, value)
}
使用组合
组合的方式比较简单
// 这个我们来扩展一个tree
type myTree struct {
node *tree.TreeNode
}
func (myNode *myTree) printf() {
myNode.node.Println()
}
默认在~/go(unix,linux),%USERPROFILE%\go(windows)
官方推荐:所有项目和第三方库都放在同一个GOPATH下
也可以将每个项目放在不同的GOPATH
go get 获取第三方库 (用这个命令可以获取到github上的包)
gopm get -v 路径 (这个命令可以获取谷歌网站的包)
go build 来编译
go install 产生pkg文件和可执行文件
go run直接编译运行