命令记不住

登陆

Go 接口

go 接口

1.接口是什么

接口是一组方法签名。当一个类型为接口中的所有方法提供定义时,它被称为实现该接口 Interface{} 可以理解成空接口,可以接收任意类型的参数

接口类型是对其他类型行为的概括与抽象,Go语言的接口在于它是隐式实现,对于一个具体的类型,无需声明它实现了哪些接口,只需要提供接口所必须的方法即可

只要是实现了方法体 GetName 的结构体都是实现了 Animal 这个接口

  • 接口的实现
type Animal interface {
	GetName() string
}
  • 接口接收参数的类型判断

接口可能为 nil,所以需要做 nil 判断

func E(x interface{}) {
	if x == nil {
		fmt.Println("empty interface")
		return
	}
	fmt.Println("non-empty interface")
}
func main() {
	var x *int = nil
	E(x)
}
  • 接口定义和使用
package main

import "fmt"

/*
type 接口名字 interface{
	方法声明
	方法名 (参数) 返回值
}
 */


type Person2 struct {
	Name string
	Age  int
}

//定义两个子结构体
type Teacher struct {
	Person2
	Subject string
}
type Student2 struct {
	Person2
	Score int
}
//定义两个结构体方法
func (t *Teacher) SayHi(){
	fmt.Printf("大家好,我是%s,我今年%d岁,我教%s\n",t.Name,t.Age,t.Subject)
}
func (s *Student2) SayHi(){
	fmt.Printf("大家好,我是%s,我今年%d岁,我的成绩是%d\n",s.Name,s.Age,s.Score)
}

//定义接口
type Humaner interface {
	SayHi()
}

func main(){
	tea := Teacher{Person2{"望合",18},"Go语言编程"}
	stu := Student2{Person2{"Ops",30},100}

	//创建接口类型变量
	var h Humaner

	//将对象的地址赋值给接口类型
	h = &stu
	h.SayHi()

	h = &tea
	h.SayHi()
}

C:\Users\vSphere\go\src\day04>go run 04.go
大家好,我是Ops,我今年30岁,我的成绩是100
大家好,我是望合,我今年18岁,我教Go语言编程

  • 多态 将接口类型作为函数参数
package main

import "fmt"

/*
type 接口名字 interface{
	方法声明
	方法名 (参数) 返回值
}
 */

type Person2 struct {
	Name string
	Age  int
}

//定义两个子结构体
type Teacher struct {
	Person2
	Subject string
}
type Student2 struct {
	Person2
	Score int
}
//定义两个结构体方法
func (t *Teacher) SayHi(){
	fmt.Printf("大家好,我是%s,我今年%d岁,我教%s\n",t.Name,t.Age,t.Subject)
}
func (s *Student2) SayHi(){
	fmt.Printf("大家好,我是%s,我今年%d岁,我的成绩是%d\n",s.Name,s.Age,s.Score)
}

//定义接口
type Humaner interface {
	SayHi()
}

//多态 将接口类型作为函数参数
func SayHi(h Humaner){
	h.SayHi()
}

func main(){
	//初始化对象
	tea := Teacher{Person2{"望合",18},"Go语言编程"}
	stu := Student2{Person2{"Ops",30},100}
	//使用多态
	SayHi(&tea)
	SayHi(&stu)
}

C:\Users\vSphere\go\src\day04>go run 04.go
大家好,我是望合,我今年18岁,我教Go语言编程
大家好,我是Ops,我今年30岁,我的成绩是100

2.实现接口

一个接口类型定义了一套方法,如果一个具体类型要实现该接口,那么必须要实现接口类型定义中的所有方法

type Reader interface {
	Read(p []byte) (n int, err error)
}
type Closter interface {
	Close() error
}

//通过组合已有接口得到的新新接口

type ReadWriter interface {
	Reader
	Closter
}

以上的语法称为嵌入式接口

type ReadWriter interface {
	Read(p []byte) (n int, err error)
	Write(p []byte) (n int, err error)
}

// 混用
type ReadWriters interface {
	Read(p []byte) (n int, err error)
	Closter
}

三种声明的效果都是一致的,方法定义的顺序也是毫无意义,真正有意义的只有接口的方法集

如果一个类型实现了一个接口要求的所有方法,那么这个类型实现了这个接口

接口的赋值规则,仅当一个表达式实现了一个接口时,这个表达式才可以赋给该接口

var w io.Writer
w = os.Stdout         //OK,*os.File有Write方法
w = new(bytes.Buffer) //OK,*bytes.Buffer有Write方法
w = time.Second       //编译错误:time.Duration缺少Write方法

var rwc io.ReadWriteCloser
rwc = os.Stdout         //OK,*os.File有Read,Write,Close方法
rwc = new(bytes.Buffer) //编译错误,缺少Close方法

//当右侧表达式也是一个接口时,该规则也有效
w = rwc
空接口,空接口对其实现类型没有任何要求,所以可以把任何值赋给空接口类型

非空的接口类型通常由一个指针类型来实现,特别是当接口类型的一个或多个方法暗示会修改接收者的情形,一个指向结构体的指针才是最常见的方法接收者

示例,flag.Value解析参数

默认的睡眠时间是1s,但是可以用-period命令标志来控制,flag.Duration函数创建了一个time.Duration类型的标志变量
//!+sleep
var period = flag.Duration("period", 1*time.Second, "sleep period")

func main() {
	flag.Parse()
	fmt.Printf("Sleeping for %v...", *period)
	time.Sleep(*period)
	fmt.Println()
}
type Celsius float64
type Fahrenheit float64

func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9.0/5.0 + 32.0) }
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32.0) * 5.0 / 9.0) }

func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }

type celsiusFlag struct{ Celsius }

func (f *celsiusFlag) Set(s string) error {
	var unit string
	var value float64
	fmt.Sscanf(s, "%f%s", &value, &unit) // no error check needed
	switch unit {
	case "C", "°C":
		f.Celsius = Celsius(value)
		return nil
	case "F", "°F":
		f.Celsius = FToC(Fahrenheit(value))
		return nil
	}
	return fmt.Errorf("invalid temperature %q", s)
}

func CelsiusFlag(name string, value Celsius, usage string) *Celsius {
	f := celsiusFlag{value}
	flag.CommandLine.Var(&f, name, usage)
	return &f.Celsius
}
var temp = tempconv.CelsiusFlag("temp", 20.0, "the temperature")

func main() {
	flag.Parse()
	fmt.Println(*temp)
}

3.接口方法集

  • 接口方法集
package main

type Animal interface {
	GetName() string
}
type People struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func (p *People) GetName() string {
	return p.Name
}
  • 调用情况
var n Animal
p1 := People{}
p2 := &People{}
n = p1 //无法编译
n = p2

4.接口方法集调用关系

仅影响接口,与实例或者指针调用无关

接口方法集定义了接口接受的规则规范里面描述的方法集

值 方法接收者
T (t T)
*T (t T) && (t *T)
T 包含匿名字段 S (t T) && (s S)
T 包含匿名字段 *S (t T) && (s S) && (s *S)
*T 包含匿名字段 *S (t T) && (t *T) && (s S) && (s *S)
  • 判断某个值是不是某个接口实现
var o interface{} = &People{Name: "LiLei"}
if n, ok := o.(Animal); ok {
	fmt.Println(n.GetName())
}
var o interface{} = &People{Name: "LiLei"}
switch o.(type) {
case People:
	fmt.Println("is people")
case *People:
	fmt.Println("is people *")
case Animal:
	fmt.Println("is animal")
}

输出为 is people *

如果没有选项 case *People 情况下,会输出 is animal,因为 People 实现了接口 Animal

People 和*People 是不同的类型,需要注意

  • 类型断言(一)
package main

import "fmt"

type people struct {
	Name string
}

func main(){
	var i interface{} = &people{"墨持"}

	//类型断言
	if v,ok := i.(*people);ok{
		fmt.Print(v.Name)
	}else{
		fmt.Println("类型匹配失败")
	}

}
C:\Users\vSphere\go\src\day04>go run 05.go
墨持

  • 类型断言(二)
package main

import (
	"fmt"
)

type people struct {
	Name string
}

func main(){
	var i interface{} = people{"乘志"}

	switch i.(type) {
	case people:
		fmt.Println("people")
	case *people:
		fmt.Println("*people")
	default:
		fmt.Println("类型匹配失败")

	}
}
C:\Users\vSphere\go\src\day04>go run 05.go
people

5.接口值

一个接口类型的值其实有2个部分:

  • 一个具体类型
  • 该类型的一个值
var w io.Writer
w = os.Stdout
w = new(bytes.Buffer)
w = nil
注意:含有空指针的非空接口

空的接口值与仅仅动态值为nil的接口值是不一样的

对于某些类型,比如os.File,空接收值是合法的,但对于bytes.Buffer则不行,方法尽管被调用了,但在尝试访问缓冲区时崩溃了

问题在于尽管一个空的*bytes.Buffer指针拥有的方法满足了该接口,但它并不满足该接口所需的一些行为,既接收者不能为空.

6.使用sort.Interface来排序

原地排序算法

//!+main
type Track struct {
	Title  string
	Artist string
	Album  string
	Year   int
	Length time.Duration
}

var tracks = []*Track{
	{"Go", "Delilah", "From the Roots Up", 2012, length("3m38s")},
	{"Go", "Moby", "Moby", 1992, length("3m37s")},
	{"Go Ahead", "Alicia Keys", "As I Am", 2007, length("4m36s")},
	{"Ready 2 Go", "Martin Solveig", "Smash", 2011, length("4m24s")},
}

func length(s string) time.Duration {
	d, err := time.ParseDuration(s)
	if err != nil {
		panic(s)
	}
	return d
}

//!-main

// !+printTracks
func printTracks(tracks []*Track) {
	const format = "%v\t%v\t%v\t%v\t%v\t\n"
	tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
	fmt.Fprintf(tw, format, "Title", "Artist", "Album", "Year", "Length")
	fmt.Fprintf(tw, format, "-----", "------", "-----", "----", "------")
	for _, t := range tracks {
		fmt.Fprintf(tw, format, t.Title, t.Artist, t.Album, t.Year, t.Length)
	}
	tw.Flush() // calculate column widths and print table
}

//!-printTracks

// !+artistcode
type byArtist []*Track

func (x byArtist) Len() int           { return len(x) }
func (x byArtist) Less(i, j int) bool { return x[i].Artist < x[j].Artist }
func (x byArtist) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

//!-artistcode

// !+yearcode
type byYear []*Track

func (x byYear) Len() int           { return len(x) }
func (x byYear) Less(i, j int) bool { return x[i].Year < x[j].Year }
func (x byYear) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

//!-yearcode

func main() {
	fmt.Println("byArtist:")
	sort.Sort(byArtist(tracks))
	printTracks(tracks)

	fmt.Println("\nReverse(byArtist):")
	sort.Sort(sort.Reverse(byArtist(tracks)))
	printTracks(tracks)

	fmt.Println("\nbyYear:")
	sort.Sort(byYear(tracks))
	printTracks(tracks)

	fmt.Println("\nCustom:")
	//!+customcall
	sort.Sort(customSort{tracks, func(x, y *Track) bool {
		if x.Title != y.Title {
			return x.Title < y.Title
		}
		if x.Year != y.Year {
			return x.Year < y.Year
		}
		if x.Length != y.Length {
			return x.Length < y.Length
		}
		return false
	}})
	//!-customcall
	printTracks(tracks)
}

/*
//!+artistoutput
Title       Artist          Album              Year  Length
-----       ------          -----              ----  ------
Go Ahead    Alicia Keys     As I Am            2007  4m36s
Go          Delilah         From the Roots Up  2012  3m38s
Ready 2 Go  Martin Solveig  Smash              2011  4m24s
Go          Moby            Moby               1992  3m37s
//!-artistoutput

//!+artistrevoutput
Title       Artist          Album              Year  Length
-----       ------          -----              ----  ------
Go          Moby            Moby               1992  3m37s
Ready 2 Go  Martin Solveig  Smash              2011  4m24s
Go          Delilah         From the Roots Up  2012  3m38s
Go Ahead    Alicia Keys     As I Am            2007  4m36s
//!-artistrevoutput

//!+yearoutput
Title       Artist          Album              Year  Length
-----       ------          -----              ----  ------
Go          Moby            Moby               1992  3m37s
Go Ahead    Alicia Keys     As I Am            2007  4m36s
Ready 2 Go  Martin Solveig  Smash              2011  4m24s
Go          Delilah         From the Roots Up  2012  3m38s
//!-yearoutput

//!+customout
Title       Artist          Album              Year  Length
-----       ------          -----              ----  ------
Go          Moby            Moby               1992  3m37s
Go          Delilah         From the Roots Up  2012  3m38s
Go Ahead    Alicia Keys     As I Am            2007  4m36s
Ready 2 Go  Martin Solveig  Smash              2011  4m24s
//!-customout
*/

// !+customcode
type customSort struct {
	t    []*Track
	less func(x, y *Track) bool
}

func (x customSort) Len() int           { return len(x.t) }
func (x customSort) Less(i, j int) bool { return x.less(x.t[i], x.t[j]) }
func (x customSort) Swap(i, j int)      { x.t[i], x.t[j] = x.t[j], x.t[i] }

//!-customcode

func init() {
	//!+ints
	values := []int{3, 1, 4, 1}
	fmt.Println(sort.IntsAreSorted(values)) // "false"
	sort.Ints(values)
	fmt.Println(values)                     // "[1 1 3 4]"
	fmt.Println(sort.IntsAreSorted(values)) // "true"
	sort.Sort(sort.Reverse(sort.IntSlice(values)))
	fmt.Println(values)                     // "[4 3 1 1]"
	fmt.Println(sort.IntsAreSorted(values)) // "false"
	//!-ints
}

7.http.Handler接口

一个程序,有两个不同的Web服务器,在不同的端口监听,定义不同的URL,分发到不同的处理程序,只需要简单地构造另外一个ServerMux,再调用一次ListenAdnServer即可.

func main() {
	db := database{"shoes": 50, "socks": 5}
	http.HandleFunc("/list", db.list)
	http.HandleFunc("/price", db.price)
	log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

//!-main

type dollars float32

func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }

type database map[string]dollars

func (db database) list(w http.ResponseWriter, req *http.Request) {
	for item, price := range db {
		fmt.Fprintf(w, "%s: %s\n", item, price)
	}
}

func (db database) price(w http.ResponseWriter, req *http.Request) {
	item := req.URL.Query().Get("item")
	if price, ok := db[item]; ok {
		fmt.Fprintf(w, "%s\n", price)
	} else {
		w.WriteHeader(http.StatusNotFound) // 404
		fmt.Fprintf(w, "no such item: %q\n", item)
	}
}

8.error接口

error只是一个接口类型,包含返回错误消息的方法:

type error interface {
	Error() string
}

构造error最简单的方法是调用errors.New,它会返回一个包含指定的错误消息的新error实例

底层的errorString类型是一个结构,而没有直接用字符串,主要是为了避免将来无意间布局变更,满足error接口的是*errorString指针,而不是原始的errorString,主要是为了让每次New分配的error实例都互不相等.

可以直接查看error的源码

推荐阅读