Go方法和接口

1. 方法

GO没有类,不过可以通过为结构体类型定义方法。

方法只是一类带特殊的接收者参数的函数

1.1 方法的声明

在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附 加到这种类型上,即相当于为这种类型定义了一个独占的方法。

import "math"

type Point struct {x, y float64}

// 计算两个点之间的距离

// 函数
func Distance(p, q Point) float64{
	return math.Hypot(q.x-p.x, q.y-p.y)
}

// 方法
func (p Point) Distance(q Point) float64{
	return math.Hypot(q.x-p.x, q.y-p.y)
}

上面的代码里那个附加的参数p,叫做方法的接收器(receiver).

在Go语言中,我们并不会像其它语言那样用this或者self作为接收器;我们可以任意的选择接 收器的名字。由于接收器的名字经常会被使用到,所以保持其在方法间传递时的一致性和简 短性是不错的主意。这里的建议是可以使用其类型的第一个字母,比如这里使用了Point的首字母p。

我们也可以为非结构体类型声明方法,只需借助type关键字

type MyFloat float64

func (mf MyFloat)foo() float64{
	if(f<0){
		return float(-mf)
	}
	return float(mf)
}

1.2 基于指针对象的方法

如果想改修改接收器对象,我们可以通过指针的方式。

当接收者本省比较大时,我们也可以通过指针来声明对象。因为通过对象直接声明的时候,调用会产生一次拷贝,浪费空间。

//修改Point的坐标
type Point struct{x, y float64}

// 函数
func (p Point)modify(x, y float64){
	p.x = x
	p.y = y
}

//方法
func (p *Point) modify(x, y float64){
	p.x = x 
	p.y = y
}

func main(){
	p = Point{3,4}
	p.modify(0,0)
	fmt.Println(p) // {3, 4}
	(&p).modify1(0,0)
	fmt.Println(p) // {0, 0}
    p.modify1(9,9)
    fmt.Println(p) // {9,9}
    //不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型 进行调用的,编译器会帮你做类型转换。
}

不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型 进行调用的,编译器会帮你做类型转换。

nil是一个合法的接收器类型

就像一些函数允许nil指针作为参数一样,方法理论上也可以用nil指针作为其接收器,尤其当 nil对于对象来说是合法的零值时,比如map或者slice。在下面的简单int链表的例子里,nil代表的是空链表:

type IntList strcut{
	value int
	next *IntList
}

//求链表的和
func (list *intList) Sum() int {
	if(list == nil){
		return 0
	}
	return list.value + list.next.Sum()
}

1.3 方法值和方法表达式

p.Distance()我们可以将其分成两步执行。p.Distance叫做选择器,选择器会返回一个方法“值”:一个将方法(p.Distance)绑定到特定接收器变量的函数。这个函数可以不通过指定其接收器即可被调用:即调用的时候不用指定接收器,因为前面声明的时候已经指定了。

p := Point{1, 2}
q := Point{4, 6}

distanceFromP := p.Distance // distanceFromP方法值  p.Distance方法表达式
fmt.Println(distanceFromP(q)) // "5"
var origin Point // {0, 0}
fmt.Println(distanceFromP(origin)) // "2.23606797749979", sqrt(5)

1.4 封装

一个对象的变量或者方法如果对调用方是不可见的话,一般就被定义为“封装”。封装有时候也 被叫做信息隐藏,同时也是面向对象编程最关键的一个方面。

Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。这种限制包内成员的方式同样适用于struct或者一个类型的方法。因而如果我 们想要封装一个对象,我们必须将其定义为一个struct。

这种基于名字的手段使得在语言中最小的封装单元是package,而不是像其它语言一样的类 型。一个struct类型的字段对同一个包的所有代码都有可见性,无论你的代码是写在一个函数 还是一个方法里。

2. 接口

接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一 起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。 很多面向对象的语言都有相似的接口概念,但Go语言中接口类型的独特之处在于它是满足隐 式实现的。也就是说,我们没有必要对于给定的具体类型定义所有满足的接口类型;简单地 拥有一些必需的方法就足够了。这种设计可以让你创建一个新的接口类型满足已经存在的具 体类型却不会去改变这些类型的定义;当我们使用的类型来自于不受我们控制的包时这种设 计尤其有用。

接口类型是一种抽象的类型。它不会暴露出 它所代表的对象的内部值的结构和这个对象支持的基础操作的集合;它们只会展示出它们自 己的方法。

posted @ 2020-11-26 18:49  You__You  阅读(169)  评论(0编辑  收藏  举报