zoukankan      html  css  js  c++  java
  • Go 方法和接口

    方法

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

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

    package main
    
    import (
    	"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 main
    
    import (
    	"fmt"
    	"math"
    )
    
    type MyFloat float64
    
    func (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 main
    
    import (
    	"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 main
    
    import (
    	"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 float64
    
    func (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 main
    
    import (
    	"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
    ")
    }
    

    错误

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

    type error interface {
        Error() string
    }

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

    package main
    
    import (
    	"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)
    	}
    }
    
  • 相关阅读:
    linux平台下Hadoop下载、安装、配置
    C#实现单例,保证线程安全
    编写response生成图片验证码时,报import com.sun.image.codec.jpeg.JPEGCodec;
    Java中static是什么意思,有什么作用?
    使用内部类和闭包
    在数据结构中存储对象
    Java学习——响应用户输入
    提升代码质量——结构化编程
    各种排序总结(六)归并排序
    各种排序总结(五)快速排序
  • 原文地址:https://www.cnblogs.com/liyuzhao/p/3887809.html
Copyright © 2011-2022 走看看