首页 > 代码库 > Go 方法和接口

Go 方法和接口

方法

Go 没有类。然而,仍然可以在结构体类型上定义方法。

方法接收者 出现在 func 关键字和方法名之间的参数中。

 

package mainimport (	"fmt"	"math")type Vertex struct {	X, Y float64}func (v *Vertex) Abs() float64 {	return math.Sqrt(v.X*v.X + v.Y*v.Y)}func main() {	v := &Vertex{3, 4}	fmt.Println(v.Abs())}

 

方法(续)

事实上,可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体。

不能对来自其他包的类型或基础类型定义方法。

package mainimport (	"fmt"	"math")type MyFloat float64func (f MyFloat) Abs() float64 {	if f < 0 {		return float64(-f)	}	return float64(f)}func main() {	f := MyFloat(-math.Sqrt2)	fmt.Println(f.Abs())}

 

接收者为指针的方法

方法可以与命名类型或命名类型的指针关联。

刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在 MyFloat 值类型上。 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。

尝试修改 Abs 的定义,同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。

 v 是 Vertex 的时候 Scale 方法没有任何作用。`Scale` 修改 `v`。当 v 是一个值(非指针),方法看到的是 Vertex 的副本,并且无法修改原始值。

Abs 的工作方式是一样的。只不过,仅仅读取 `v`。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。

package mainimport (	"fmt"	"math")type Vertex struct {	X, Y float64}func (v *Vertex) Scale(f float64) {	v.X = v.X * f	v.Y = v.Y * f}func (v *Vertex) Abs() float64 {	return math.Sqrt(v.X*v.X + v.Y*v.Y)}func main() {	v := &Vertex{3, 4}	v.Scale(5)	fmt.Println(v, v.Abs())}

 

接口

接口类型是由一组方法定义的集合。

接口类型的值可以存放实现这些方法的任何值。

注意: 左边的代码会导致编译失败。

由于 Abs 只定义在 *Vertex 上,而不是 `Vertex`。 所以 Vertex 不满足 `Abser`。

package mainimport (	"fmt"	"math")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 // a *Vertex 实现了 Abser	// 下面一行,v 是一个 Vertex(而不是 *Vertex)	// 所以没有实现 Abser。	a = v	fmt.Println(a.Abs())}type MyFloat float64func (f MyFloat) Abs() float64 {	if f < 0 {		return float64(-f)	}	return float64(f)}type Vertex struct {	X, Y float64}func (v *Vertex) Abs() float64 {	return math.Sqrt(v.X*v.X + v.Y*v.Y)}

 

隐式接口

类型通过实现那些方法来实现接口。

没有显式声明的必要。

隐式接口解藕了实现接口的包和定义接口的包:互不依赖。

因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。

包 io 定义了 Reader 和 `Writer`;其实不一定要这么做。

package mainimport (	"fmt"	"os")type Reader interface {	Read(b []byte) (n int, err error)}type Writer interface {	Write(b []byte) (n int, err error)}type ReadWriter interface {	Reader	Writer}func main() {	var w Writer	// os.Stdout 实现了 Writer	w = os.Stdout	fmt.Fprintf(w, "hello, writer\n")}

 

错误

错误是可以用字符串描述自己的任何东西。主要思路是由预定义的内建接口类型 `error`,和方法 `Error`,返回字符串:

type error interface {    Error() string}

当用 fmt 包的多种不同的打印函数输出一个 error 时,会自动的调用该方法。

package mainimport (	"fmt"	"time")type MyError struct {	When time.Time	What string}func (e *MyError) Error() string {	return fmt.Sprintf("at %v, %s",		e.When, e.What)}func run() error {	return &MyError{		time.Now(),		"it didn‘t work",	}}func main() {	if err := run(); err != nil {		fmt.Println(err)	}}