方法和接口
方法
- Go没有类,但可以结构体定义方法
- 方法就是一类带特殊的==接受者==的函数,接受者可以是命名类型或结构体类型的一个值或一个指针
- 方法接收者在它自己的参数列表内,位于func关键字和方法名之间
type Vertex struct{
X,Y float64
}
func (v vertex)Abs()float64{
return math.Sqrt(v.X*v.X v.Y*v.Y)
}//Abs方法的接受者为v,类型为Vertex
func main (){
v:=Vertex{3,4}
}
fmt.Println(v.Abs())//输出为5
方法即函数
- 方法只是带接受者的函数
- 把上面的Abs函数写为正常函数为,功能不变
func Abs(v Vertex)float64{
return math.Sqrt(v.X*v.X v.Y*v.Y)
}
方法(续)
- 也可以声明非结构体类型的声明方法
- 接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法
type MyFloat float64
func (f MyFloat)Abs()float64{//带 Abs 方法的数值类型 MyFloat。
if(f<0)
return float64(-f)
else
return float64(f)
}
指针接收者
- 可以为指针接收者声明方法
- 指针接收者的方法可以修改接收者指向的值
type Vertex struct {
X,Y float64
}
func (v*Vertex)Abs(f float64){
v.X=v.X*f
v.Y=v.Y*f
}
指针与函数
- 带指针参数的函数必须接受一个指针:func add(v *Vertex)int
- 以指针为接收者的方法被调用时,接收者既能为值又能为指针
选择 值or指针 为接收者
使用指针作为接收者的原因
- 方法能够修改接收者指向的值
- 避免每次调用方法时复制该值
接口
- 接口类型是由一组方法签名定义的集合
- 接口类型的变量可以保存任何实现了这些方法的值
type Abser interface{
Abs()float64
}
func main (){
var a Abser
f:=MyFloat(-math.Sqrt2)
v:=Vertex{3,4}
a=f//a MyFloat实现了Abser
a=&v//*Vertex实现了Abser
}
接口的隐式实现
- 类型通过实现一个接口的所有方法来实现该接口
- 隐式接口从接口的实现中解耦了定义,这样接口的实现可以出现在任何包中
- 因为无需在每一个实现上增加新的接口名称,可以同时也鼓励了明确的接口定义
type I interface{
M()
}
type T struct {
string S
}
// 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。
func (t T)M(){
fmt.Piintln(t.S)
}
func main (){
var i I=T{"hello"}
i.M()
}
接口值
- 接口值也是值,可以像其他值一样传递
- 可以用作函数的参数或返回值
- 在内部,接口值可以看做包含值和具体类型的元组
- 接口值调用方法时会执行其底层类型的同名方法
type I interface{//定义接口类型
M()
}
type T struct{
string S
}
func (t*T)M(){//方法M
fmt.Printf(t.S)
}
type F float64
func(f F)M(){//f作为方法M()的接收者
fmt.Println(f)
}
func main (){
var i I//接口类型变量i
i=&T{"hello"}
describe(i)//接口值作为函数的参数
i.M()//接口值调用方法,与此同时,会执行其底层类型的同名方法
func describe(i I) {
fmt.Printf("(%v, %T)n", i, i)
}
}
底层值为nil的接口值
- 即使接口内的具体值为nil,方法仍会被nil接收者调用
func (t*T)M(){
if(t==nil){
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
nil接口值
- nil接口值既不保存值,也不保存具体类型
- nil接口调用方法会发生错误,因为接口的元组没有包含指明调用哪个具体的方法的类型
空接口
- 指定了零个方法的接口值为空接口
interface{}
- 空接口可以保存任何类型的值(因为每个类型都至少实现了零个方法)
- 空接口用来处理未知类型的值
类型断言
- 类型断言提供访问接口值底层具体值的方式
t:=i.(T)
- 为了判断一个接口值是否保存了一个特定类型,类型断言可返回两个值:底层值和报告断言是否成功的布尔值
t,ok:=i(T)
代码语言:javascript复制func main (){
var i interface{}="hello"
s:=i.(string)
s,ok:=i.(string)
}
类型选择
- 类型选择是一种按顺序从几个类型断言中选择分支的结构
- 类型选择与一般的switch类似,只不过case是类型而不是值
switch v:=i.(type){
case T://V的类型为T
case S:
default:
}
- 类型选择中的声明与类型断言的i.(T)相同,只是具体类型T换为了type
Stringer
- fmt包中定义的Stringer是最普遍的接口之一
type Stringer interface{
String()string
}
- Stirng可以用字符串描述自己的类型
type person struct {
Name String
Age int
}
func (p person) String () stirng{
return fmt.Sprintf("%v,(%v years)",P.Name,P.Age)
}
func main (){
a:=person{"bob",32}
b:=person{"tom",12}
fmt.Println(a,b)
}
错误
- error表示错误状态
- 和fmt.Sprintf相似,error只是一个内建接口
type error interface{
Error()string
}
- 通常函数会返回一个error值,调用它的代码应该判断这个错误是否等于nil来错误处理
- error为nil表示成功,非nil的error表示失败
Reader
- io包指定了io.reader接口,表示从数据流的末尾进行读取
- io.reader的一个接口方法
func (T)Read(b[]byte)(n int err error)
- read用数据填充给定的字节切片并返回填充的字节数和错误值。在遇到数据流结尾时,会返回一个io.EOF错误
图像
- image包定义了image接口
package image
type Image interface{
ColorModel()color.Model
Bounds()Rectangle
At(x,y int)color.Color
}
代码语言:javascript复制import (
"fmt"
"image"
)
func main() {
m := image.NewRGBA(image.Rect(0, 0, 100, 100))
fmt.Println(m.Bounds())
fmt.Println(m.At(0, 0).RGBA())
}