• go学习(三、面向对象)


    3.1 结构体和方法

    go语言仅支持封装,不支持继承和多态。

    go更没有c++的虚函数啥的。

    所以go只有struct,没有class,这个倒是有点像c。

    3.1.1 结构体定义和使用

    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)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    go语言没有构造函数,这个感觉是因为go只支持struct的,不支持类,struct只又变量,所以变量可以赋初值就可以了。

    当然如果想自己写构造函数,那就跟c语言一样,写一个工厂函数,返回一个类对象。

    func creatNode(value int) *TreeNode {
        return &TreeNode{Value : value}
    }
    
    • 1
    • 2
    • 3

    这种玩法,让我想起了当初用c语言来搞面向对象,就是这种感觉。

    注意:如果按c++的标准,这个是返回了局部变量,c++的局部遍历是存储在栈上的,返回指针是有问题的,但是go语言不存在这个问题,因为go语言根据程序员的写法,自动推断这个变量是存储在栈上还是堆上。

    3.1.2 方法定义

    接下来我们看看go的struct的方法怎么定义:

    func (node TreeNode) println() {
        fmt.Println(node)
    }
    
    • 1
    • 2
    • 3

    把类型定义在前面,这个在go语言中的说法是接收者,也就是我调用的时候:

    node.println()    // node对象会传给这个println函数,printfln函数会接收这个node对象
    
    • 1

    这种方式是值接收者,是不可以修改对象中的值的,只有指针接收者才能修改变量的值:

    func (node *TreeNode) setValue(value int) {
        node.Value = value
    }
    
    • 1
    • 2
    • 3

    看着其实问题不大,就改成一个指针。

    其他语言的接收者,也就是this指针,其实都是传指针,只有go语言支持两个。

    注意:go语言nil是可以调用的,只不过是取不到值的

    var pRoot *TreeNode
    pRoot.setValue(33)     // 这是可以执行的,调用到函数内部的,需要在函数内部判断
    
    
    func (node *TreeNode) setValue(value int) {
        if node == nil {     // 这里加判断就可以了
            return 
        }
        node.Value = value
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果是c++语言的话,需要在指针调用的时候,就需要判断了。

    3.1.3 值接受者vs 指针接收者

    • 要改变内容必须使用指针接收者

    • 结构过大也考虑使用指针接收者

    • 一致性:如有指针接收者,最好都是指针接收者

    3.2 包和封装

    3.2.1 封装

    • 名字一般使用CamelCase(驼峰法)

    • 首字母大写:public (公有方法或变量)

    • 首字母小写:private (私有方法或变量)

    3.2.2 包

    • 每个目录一个包(包名和目录名可以不一样,但是一个目录只能有一个包)

    • main包包含可执行入口(其他包可以写main函数么,可以试试)

    • 为结构定义的方法必须放在同一个包内(可以多文件,但一定要在一个包内)

    3.2.3 示例

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这是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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    总结两点:包引入记得是文件夹名,包的文件名可以跟包名不一样。

    3.2.4 扩展别人的类型

    如果是其他语言,扩展别人类型,是可以通过继承的方式来扩展。

    因为go语言没有继承,所以我们来看看go语言是怎么扩充别人的类型。

    • 定义别名

      使用别名,就难了一点点

      // 使用别名
      type queue []int       // 用type来定义[]int别名
      
      func (q *queue) push(value int) {    // 封装好了就各种操作
      	*q = append(*q, value)
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 使用组合

      组合的方式比较简单

      // 这个我们来扩展一个tree
      type myTree struct {
      	node *tree.TreeNode
      }
      
      func (myNode *myTree) printf() {
      	myNode.node.Println()
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    3.3 GOPATH学习

    3.3.1 GOPATH环境变量

    • 默认在~/go(unix,linux),%USERPROFILE%\go(windows)

    • 官方推荐:所有项目和第三方库都放在同一个GOPATH下

    • 也可以将每个项目放在不同的GOPATH

    3.3.2 go命令

    go get 获取第三方库 (用这个命令可以获取到github上的包)

    gopm get -v 路径 (这个命令可以获取谷歌网站的包)

    go build 来编译

    go install 产生pkg文件和可执行文件

    go run直接编译运行

  • 相关阅读:
    flutter iOS 缺少通知权限,缺少位置权限
    English语法_介词 - in
    Flink测试利器之DataGen初探 | 京东云技术团队
    Contextual Transformer Networks for Visual Recognition
    MySQL GROUP BY和HAVING的使用 2022/09/09
    使用ensp搭建路由拓扑,并使用ospf协议实现网络互通实操
    剑指offer链表篇
    机器学习(七):sklearn转换器估计器及K-近邻算法
    npm常用命令系统介绍
    vue2循环的列表商品选择后的商品禁用
  • 原文地址:https://blog.csdn.net/C1033177205/article/details/125471893