【Day 3】跟着Google学Go——A Tour of Go

本文介绍了Go语言中没有类但可以通过结构体定义方法,方法接收器可以是值或指针,指针接收器允许修改接收器的值。文章还讨论了非结构体类型的方法声明限制,并展示了如何为数值类型定义方法。此外,文章讲解了Go中的接口,强调了隐式接口实现以及接口值和方法调用。最后,文章通过实例展示了类型断言、类型选择以及错误处理和io.Reader接口的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
图片来源:samjoule/Shutterstock


Day 3:

Go没有类的概念,但是可以为结构体类型定以方法
方法就是有特殊接收器参数的函数
方法接收者在它自己的参数列表内,位于 func 关键字和方法名之间
此例中,Abs方法有一个Vertex类型的接收器v
在这里插入图片描述
方法仅仅是一个拥有接收器参数的函数
这里的代码实现了一个与方法功能一致的普通函数
在这里插入图片描述
也可以为非结构体类型声明方法
此例中我们看到一个拥有Abs方法的数值类型MyFloat
注意:只能在类型与方法在同一个包中的情况下声明方法,而不能在两者不在同一个包的情况下声明方法甚至内置类型(如int)也不行

package main

import (
	"fmt"
	"math"
)

func (f float64) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

func main() {
	f := float64(-math.Sqrt2)
	fmt.Println(f.Abs())
}
./prog.go:8:6: cannot define new methods on non-local type float64
./prog.go:17:15: f.Abs undefined (type float64 has no field or method Abs)

在这里插入图片描述
可以使用指针接收器声明方法
对于某些类型T,接收器的类型可以使用T文法(类型T本身不能是指针例如int)
拥有指针接收器的方法可以修改指针接收器指向的值。由于方法经常需要修改它们的接收器,所以指针接收器比值接收器更常见。
如果把Scale方法的指针接收器换成和Abs方法一样的值接收器,则接收到的v仅是原始值的拷贝,因而无法实现对其修改。
在这里插入图片描述
功能不变,把之前方法重写成函数
在这里插入图片描述
函数中如果有指针参数则必须要传入指针,而指针作为接收器的方法被调用时,接收器可以是指针也可以是值。
例如v.Scale(5),假设v是一个值而不是指针,指针作为接收器的方法Scale会自动被调用,这是因为当Scale方法有指针接收器时,Go会将v.Scale(5)解释为(&v).Scale(5)
在这里插入图片描述
反过来如果函数需要值参数,则必须要传入对应类型的值,而值作为接收器的方法被调用时,接收器可以是指针也可以是值
本例中p.Abs()被解释为(*p).Abs()
在这里插入图片描述
更多的情况是使用指针接收器而不是值接收器,主要原因有两点:

  1. 方法可以修改接收器所指向的值
  2. 避免每次方法调用时拷贝值所带来的性能损耗,尤其是接收器为比较大的结构体时

本例中Scale和Abs方法都有指针接收器,但是Abs方法不需要修改接收器的值
通常,所有给定类型的方法都需要有值或者指针接收器,但不能同时存在
在这里插入图片描述
接口类型 是由一组方法签名定义的集合。
接口类型的变量可以保存任何实现了这些方法的值。
这里需要一些背景知识,否则不太好理解https://blog.youkuaiyun.com/MacwinWin/article/details/112789278
在这里插入图片描述
一个类通过实现接口的所有方法实现(implement)该接口,没有专门的显示声明,就没有“implements”关键字
隐式接口使接口的定义和实现解耦,接口可以在没有预先安排的情况下出现在任何包中。由于这种类似于“鸭子类型”的特性,使得不必为每个类定义接口
在这里插入图片描述
在内部,接口值可以被理解为值和对应类型的元组
接口值保存了一个具体底层类型的值
在接口上执行某方法会调用其底层类型的同名方法
在这里插入图片描述
即便接口的具体值为nil,仍然会调用nil接收器的方法
在某些语言中这会出发空指针异常,但在 Go 中通常会写一些方法来优雅地处理它
注意:保存nil值的接口本身是非nil的
在这里插入图片描述
nil接口的值既没有值也没有具体类型
调用nil接口的方法会导致运行时错误,这是因为该接口内部没有指明调用哪个具体方法的类型
在这里插入图片描述
指定了0个方法的接口被称为空接口
空接口可保存任何类型的值(因为每个类型都至少实现了0个方法)
空接口被用来处理未知类型的值
在这里插入图片描述
类型断言提供了访问接口值底层值的方法

t := i.(T)

这条语句断言接口值i保存具体方法T并将底层类型为T的值赋给变量t
如果i没有保存类型为T的值,则语句会触发一个恐慌

为了测试一个接口值是否保存一个特定的类型,将类型断言可返回两个值:

  • 底层值
  • 是否断言成功的布尔值
t, ok := i.(T)

如果i保存有T类型的值,则t将为底层值,ok将为true
反之,ok将为false,t将为类型T的0值,恐慌不会被触发
注意这里和测试映射中是否存在某键的语法很像

elem, ok = m[key]

在这里插入图片描述
类型选择是一种允许按一定顺序从几个断言中选择分支的结构
类型选择类似常规的switch语句,但是类型选择中的case为类型而非值,并且那些值被与提供的接口值所保存的值的类型进行比较
类型选择中的声明与类型断言的语法一致,但是类型T被关键字type替代
switch语句测试是否接口值i保存类型T或S的值,在每个case语句中,变量v将会是类型T或S并且保存i的值;在default case语句中,变量 v 与i的接口类型和值相同
在这里插入图片描述
fmt包中定义的Stringer接口是最普遍的接口之一
Stringer类型可以描述它自身为一个string,fmt包和其他许多包依赖这个接口打印值
在这里插入图片描述
练习比较简单,就是重新实现fmt.Stringer接口中的String方法,实现格式化打印字符串
在这里插入图片描述
Go程序使用error值来表示错误状态
error类型是一个内部接口,和fmt.Stringer类似
和fmt.Stringer类似,fmt包在打印值的时候也会寻找error接口
通常函数会返回error值,调用的代码需要通过判断error是否为nil来处理error
error值为nil意为成功,非nil的error值意为失败
在这里插入图片描述
比较简单的一个练习,但其中的问题比较让人费解:
在Error方法内调用fmt.Sprint(e)将会导致程序进入无限循环,而避免出现这种情况只需要转换一下e,这是为什么?
比较准确的解释是fmt.Sprintf(e)语句会调用e.Error()方法以将e转为String类型,导致函数自己调用自己的死循环,转为float64后,就没有Error()、String()方法,自然也就不会进入自己调用自己的死循环中了

参考https://stackoverflow.com/a/27475316
在这里插入图片描述
io包提供了io.Reader接口,表示从流数据的末尾进行读取
Go的标准库包含许多该接口的实现,包括文件、网络连接、压缩、加密等等
Read 用数据填充给定的字节切片并返回填充的字节数和错误值。在遇到数据流的结尾时,它会返回一个 io.EOF 错误。
示例代码创建了一个 strings.Reader 并以每次 8 字节的速度读取它的输出。
在这里插入图片描述
说实话没看懂题的意思,代码是抄的
在这里插入图片描述
首先代码是抄的,然后看懂了
第26行创建了类型为strings.NewReader的变量s
第27行创建了类型为rot13Reader的结构体变量r,由于strings.NewReader实现了io.Reader接口,所以能将变量s赋给io.Reader类型的r
第29行将os.Stdout和&r传入io.Copy函数中,由于第13行中Read方法为指针接收器,所以此处必须为&r而不能是r。没有看源码,但可以肯定的是io.Copy函数肯定调用了r.Read方法
第14行使用rot.r即s的Read函数读取"Lbh penpxrq gur pbqr!"到二进制切片b中;
第15-21行为rot13算法的实现部分,解密上面的文本
关于io.Copy可以参考源码:https://github.com/golang/go/blob/ecf4ebf10054f70e51a0ce759b2ae91aa4febd1a/src/io/io.go#L381
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值