Go学习之路:方法和接口(DAY 3)
创始人
2024-03-09 04:23:50
0

文章目录

    • 前引
    • 方法和接口
      • 1.1、方法/声明方法
      • 1.2、方法/捆绑其他类型
      • 1.3、方法/方法常用指针传递
      • 1.4、方法/普通函数指针传递和方法指针传递区别
      • 2.1、接口/方法签名集合
      • 2.2、接口/接口断言
      • 2.3、接口/switch case
      • 练习题、接口/stringer实现字符串打印
      • 2.4、接口/错误


前引


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

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

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

下面来看看有啥吧


方法和接口


1.1、方法/声明方法


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

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


下面简单写了个链表插入

package mainimport ("fmt""math/rand"
)type Node struct {val intPrev *NodeNext *Node
}func (node *Node) AddNode(nodeptr *Node) {nextptr := node.Nextprevptr := nodenextptr.Prev = nodeptrprevptr.Next = nodeptrnodeptr.Next = nextptrnodeptr.Prev = prevptr
}func main() {dummyhead, dummytail := Node{val : -1}, Node{val: -1}dummyhead.Next = &dummytaildummytail.Prev = &dummyheadfor i := 0; i < 10; i++ {randint := rand.Int() % 100fmt.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、方法/捆绑其他类型


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


在这里插入图片描述


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


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

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

所以通常捆绑的的是指针


下面测试的就是值传递

package mainimport ("fmt""math/rand"
)type Node struct {val intPrev *NodeNext *Node
}func (node Node) AddNode(nodeptr Node) {nextptr := node.Nextprevptr := &nodefmt.Printf("copyptr : %p\n", &nodeptr)nextptr.Prev = &nodeptrprevptr.Next = &nodeptrnodeptr.Next = nextptrnodeptr.Prev = prevptr
}func main() {dummyhead, dummytail := Node{val : -1}, Node{val: -1}dummyhead.Next = &dummytaildummytail.Prev = &dummyheadfor i := 0; i < 10; i++ {randint := rand.Int() % 100fmt.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}
}

下面是指针传递

package mainimport ("fmt""math/rand"
)type Node struct {val intPrev *NodeNext *Node
}func (node *Node) AddNode(nodeptr *Node) {nextptr := node.Nextprevptr := nodenextptr.Prev = nodeptrprevptr.Next = nodeptrnodeptr.Next = nextptrnodeptr.Prev = prevptr
}func main() {dummyhead, dummytail := Node{val : -1}, Node{val: -1}dummyhead.Next = &dummytaildummytail.Prev = &dummyheadfor i := 0; i < 10; i++ {randint := rand.Int() % 100fmt.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.4、方法/普通函数指针传递和方法指针传递区别


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

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

package maintype Node struct {val intPrev *NodeNext *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 := &dummynodeFuncAdd(nodeptr, 10)nodeptr.FuncPtrAdd(10)dummynode.FuncPtrAdd(10)
}

2.1、接口/方法签名集合


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

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

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


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

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

package mainimport ("fmt"
)type GeneralInterface interface {Val() intVaild() bool
}type Node struct {key intval intend 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 intvaild 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 GeneralInterfaceg_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())
}

2.2、接口/接口断言


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


下面是测试结果

package mainimport "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)
}

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


2.3、接口/switch case


interface.(type)

只针对switch case
下面是例子


package mainimport "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)}
}

在这里插入图片描述


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


****

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


示例代码

package mainimport "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)}
}

实现代码

package mainimport ("fmt"
)type IPAddr [4]bytetype 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)}
}

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


2.4、接口/错误


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

下面是简单的尝试了一下

package mainimport ("fmt""time"
)type MyError struct {When time.TimeErrstring 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)}
}

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

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

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...