• Go学习之路:方法和接口(DAY 3)



    前引


    昨天终于算是回到了家 和我爸妈好好聊了很久
    在学校的日子 艰难且难熬 学习效率极低 且吃的也是相当的糟糕
    感觉每天都是度日如年 我每天都在盼着出来的日子 能够离开学习 离开重庆的日子

    尽管距离进部门还有一些日子 而且材料也都没有填写 但是理应这些日子我应该忙些什么 而不是什么都不干
    在家里隔离三天 总算是安稳下来了 晚上还要和朋友打游戏 那么今天就把Go最后的两部分解决了吧

    原谅我厚颜无耻的把DAY 3写上 尽管隔了很多天
    如果这三天没有隔离在家里面 我还是想再回到省图书馆的 毕竟那是我留在成都的最多的回忆之地

    下面来看看有啥吧


    方法和接口


    1.1、方法/声明方法


    方法本质上就是一个普通函数 只是类似于c++中将成员函数和类中的数据结构成员分开

    方法的声明方式是 func (struct name) funcname(type) returntype {}
    和普通函数不一样的地方是在于 在参数前面多了个括号


    下面简单写了个链表插入

    package main
    
    import (
    	"fmt"
    	"math/rand"
    )
    
    type Node struct {
    	val int
    	Prev *Node
    	Next *Node
    }
    
    func (node *Node) AddNode(nodeptr *Node) {
    	nextptr := node.Next
    	prevptr := node
    	nextptr.Prev = nodeptr
    	prevptr.Next = nodeptr
    	nodeptr.Next = nextptr
    	nodeptr.Prev = prevptr
    }
    
    func main() {
    	dummyhead, dummytail := Node{val : -1}, Node{val: -1}
    	dummyhead.Next = &dummytail
    	dummytail.Prev = &dummyhead
    
    	for i := 0; i < 10; i++ {
    		randint := rand.Int() % 100
    		fmt.Printf("%d ", randint)
    		insertnode := &Node{val : randint}
    		dummyhead.AddNode(insertnode)
    	}
    	fmt.Println()
    
    	for now := dummytail.Prev; now != &dummyhead; {
    		fmt.Printf("%d ", now.val)
    		now = now.Prev
    	}
    }
    
    
    • 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

    运行效果
    在这里插入图片描述


    1.2、方法/捆绑其他类型


    1、不能捆绑基础类型 想要捆绑基础类型的方法只有利用别名
    2、只能在同一个包内创建的类型对其声明方法 不能跨包
    省流:接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法


    在这里插入图片描述


    1.3、方法/方法常用指针传递


    golang是没有引用这种说法的 c++中有引用
    如果我们是直接传输的指针 是对底层的结构体修改

    而如果值传递 就类似于值拷贝
    而如果我们要用指针的话 是修改的结构体底层的值

    所以通常捆绑的的是指针


    下面测试的就是值传递

    package main
    
    import (
    	"fmt"
    	"math/rand"
    )
    
    type Node struct {
    	val int
    	Prev *Node
    	Next *Node
    }
    
    func (node Node) AddNode(nodeptr Node) {
    	nextptr := node.Next
    	prevptr := &node
    	fmt.Printf("copyptr : %p\n", &nodeptr)
    	nextptr.Prev = &nodeptr
    	prevptr.Next = &nodeptr
    	nodeptr.Next = nextptr
    	nodeptr.Prev = prevptr
    }
    
    func main() {
    	dummyhead, dummytail := Node{val : -1}, Node{val: -1}
    	dummyhead.Next = &dummytail
    	dummytail.Prev = &dummyhead
    
    	for i := 0; i < 10; i++ {
    		randint := rand.Int() % 100
    		fmt.Printf("%d ", randint)
    		insertnode := Node{val : randint}
    		fmt.Printf("ptr : %p, ", &insertnode)
    		dummyhead.AddNode(insertnode)
    	}
    	fmt.Println()
    
    	for now := dummytail.Prev; now != nil && now != &dummyhead; {
    		fmt.Printf("%d ", now.val)
    		now = now.Prev
    	}
    }
    
    
    • 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
    • 42
    • 43

    下面是指针传递

    package main
    
    import (
    	"fmt"
    	"math/rand"
    )
    
    type Node struct {
    	val int
    	Prev *Node
    	Next *Node
    }
    
    func (node *Node) AddNode(nodeptr *Node) {
    	nextptr := node.Next
    	prevptr := node
    	nextptr.Prev = nodeptr
    	prevptr.Next = nodeptr
    	nodeptr.Next = nextptr
    	nodeptr.Prev = prevptr
    }
    
    func main() {
    	dummyhead, dummytail := Node{val : -1}, Node{val: -1}
    	dummyhead.Next = &dummytail
    	dummytail.Prev = &dummyhead
    
    	for i := 0; i < 10; i++ {
    		randint := rand.Int() % 100
    		fmt.Printf("%d ", randint)
    		insertnode := &Node{val : randint}
    		dummyhead.AddNode(insertnode)
    	}
    	fmt.Println()
    
    	for now := dummytail.Prev; now != &dummyhead; {
    		fmt.Printf("%d ", now.val)
    		now = now.Prev
    	}
    }
    
    
    • 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

    值传递 是常见另外的结构体


    1.4、方法/普通函数指针传递和方法指针传递区别


    普通指针传递 和 方法指针传递区别

    下面有例子 可以很明显发现有差别 方法相比普通函数 更像c++有类的感觉 而且最主要的是 不仅可以通过指针传递 还可以通过结构体传递 很方便
    相反则不行了

    package main
    
    type Node struct {
    	val int
    	Prev *Node
    	Next *Node
    }
    
    func (node *Node) FuncPtrAdd(addnum int) {
    	node.val += addnum
    }
    
    func FuncAdd(node* Node, addnum int) {
    	node.val += addnum
    }
    
    func main() {
    	dummynode := Node{val : 0}
    	nodeptr := &dummynode
    	
    	FuncAdd(nodeptr, 10)
    	nodeptr.FuncPtrAdd(10)
    	dummynode.FuncPtrAdd(10)
    }
    
    
    • 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

    2.1、接口/方法签名集合


    看了看接口 刚开始没看懂 之后才理解
    接口内部类似定义了方法的捆绑题 相同的函数名方法 我们是可以声明给不同的结构体或者结构体指针的

    接口的话 我们可以捆绑到不同的对象/结构体 然后使用接口的话 主要利用接口内部的方法 我们可以调用接口内部的方法

    用一个我自己理解的例子吧 可以把接口当作通用模型
    模型可以套在多个不同的对象上 只要兼容对象 然后这个模型我们能使用的是内部提供的通用方法


    简单定义接口的定义的是
    type interfacename interface { }

    下面是大概写了两个结构体 大概表示了一下上面接口的用途
    看名字确实也能明白是啥意思

    package main
    
    import (
    	"fmt"
    )
    
    type GeneralInterface interface {
    	Val() int
    	Vaild() bool
    }
    
    type Node struct {
    	key int
    	val int
    	end bool
    }
    
    func (node *Node) Val() int {
    	return node.val
    }
    
    func (node *Node) Vaild() bool {
    	return node.end
    }
    
    func (node *Node) TestVaild() bool {
    	return node.end
    }
    
    type tmpstruct struct {
    	val int
    	vaild bool
    }
    
    func (ptr *tmpstruct) Val() int {
    	return ptr.val
    }
    
    func (ptr *tmpstruct) Vaild() bool {
    	return ptr.vaild
    }
    
    func (ptr *tmpstruct) Gogo() bool {
    	return ptr.vaild
    }
    
    func main() {
    	var g_interface GeneralInterface
    	g_interface = &Node{1, 1, false}
    	fmt.Println(g_interface.Val(), g_interface.Vaild())
    
    	g_interface = &tmpstruct{2, true}
    	fmt.Println(g_interface.Val(), g_interface.Vaild())
    }
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    2.2、接口/接口断言


    判断底层接口实际存储的类型 且断言可以防止恐慌 如果断言返回的有两个值 如果底层类型和我们的断言不相同 则不会引起恐慌


    下面是测试结果

    package main
    
    import "fmt"
    
    func main() {
    	var g_interface interface{} = "hello"
    
    	s1, ok1 := g_interface.(int)
    	fmt.Println(s1, ok1)
    
    	s2, ok2 := g_interface.(string)
    	fmt.Println(s2, ok2)
    
    	s3, ok3 := g_interface.(float64)
    	fmt.Println(s3, ok3)
    
    	s4 := g_interface.(int) // 报错(panic)
    	fmt.Println(s4)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行效果
    在这里插入图片描述


    2.3、接口/switch case


    interface.(type)

    只针对switch case
    下面是例子


    package main
    
    import "fmt"
    
    func main() {
    	var g_interface interface{} = "hello"
    
    	switch s := g_interface.(type) {
    	case int:
    		fmt.Println("int type,", "value:", s)
    	case float64:
    		fmt.Println("float64 type,", "value:", s)
    	case float32:
    		fmt.Println("float32 type,", "value:", s)
    	default:
    		fmt.Println("other type,", "value:", s)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述


    练习题、接口/stringer实现字符串打印


    ****

    通常对于fmt包 内部会调用接口Stringer String 只要我们实现了接口对应的函数 就会调用我们这边的String 之后对于我们自定义的类 我就可以去定义这样的类了


    示例代码

    package main
    
    import "fmt"
    
    type IPAddr [4]byte
    
    // TODO: 给 IPAddr 添加一个 "String() string" 方法
    
    func main() {
    	hosts := map[string]IPAddr{
    		"loopback":  {127, 0, 0, 1},
    		"googleDNS": {8, 8, 8, 8},
    	}
    	for name, ip := range hosts {
    		fmt.Printf("%v: %v\n", name, ip)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    实现代码

    package main
    
    import (
    	"fmt"
    )
    
    type IPAddr [4]byte
    
    type Stringer interface {
    	String() string
    }
    
    func (ptr IPAddr) String() string {
    	return fmt.Sprintf("%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3])
    }
    
    func main() {
    	hosts := map[string]IPAddr{
    		"loopback":  {127, 0, 0, 1},
    		"googleDNS": {8, 8, 8, 8},
    	}
    	for name, ip := range hosts {
    		fmt.Println(name, ip)
    	}
    }
    
    • 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

    实现效果
    在这里插入图片描述


    2.4、接口/错误


    内部输出打印的时候 都采用的接口的方式
    对应结构体字符串打印化 是采用的String() string
    而对应错误打印的话 则是采用的Error() string

    下面是简单的尝试了一下

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    type MyError struct {
    	When time.Time
    	Errstring string
    }
    
    func (error MyError) Error() string {
    	return fmt.Sprintf("%v, %s", error.When, error.Errstring)
    }
    
    func RunError() error {
    	return MyError{time.Now(), "Error Occur!"}
    }
    
    func main() {
    	if err := RunError(); err != nil {
    		fmt.Println(err)
    	}
    }
    
    • 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

    实现效果
    在这里插入图片描述

    ======================
    后续还有一些 就不写了
    都是其他的接口练习 有兴趣的可以尝试一下

  • 相关阅读:
    制作linux系统内部yum源仓库
    Unity 游戏设计模式:观察者模式
    三层交换机
    pyecharts 主题:颜色渐变实例(线性渐变)
    【知识网络分析】作者合作网络(Co-authorship)
    什么?“裸辞”一个月拿到13家offer,网友:你是在找存在感吗···
    java毕业设计“小蜜蜂”校园代取快递系统mybatis+源码+调试部署+系统+数据库+lw
    http-response返回数据被截断,返回不完全
    Ansys Maxwell三相变压器制作方法教程
    外包干了3个月,技术倒退明显...
  • 原文地址:https://blog.csdn.net/qq_37500516/article/details/128126407