golang_接口

 目录

说明

接口使用

一些例子

说明

接口本身是调用方和实现方均需要遵守的一种协议,大家按照统一的方法命名参数类型和数量来协调逻辑处理的过程。

Go 语言中使用组合实现对象特性的描述。对象的内部使用结构体内嵌组合对象应该具有的特性,对外通过接口暴露能使用的特性。

Go 语言的接口设计是非侵入式的,接口编写者无须知道接口被哪些类型实现。而接口实现者只需知道实现的是什么样子的接口,但无须指明实现哪一个接口。编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。

语法

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2
    …
}

- 接口类型名:使用 type 将接口定义为自定义的类型名。Go 语言的接口在命名时,一般会在单词后面添加 er,如有写操作的接口叫 Writer,有字符串功能的接口叫 Stringer,有关闭功能的接口叫 Closer 等。
- 方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略
type myinterface interface{
	Func1()  // 无参无返回值
	Func2(a int)  // 有参无返回值
	Func3() (string,int)  // 无参有返回值
	Func4(a string, b int) (bool)  // 有参有返回值
}
接口定义示例

接口实现的条件

- 接口的方法与实现接口的类型方法格式一致
  - 函数名须一致,不一致则报错
  - 实现接口的方法签名须一致,不一致则报错
- 接口中所有方法均被实现
package main

import "fmt"

type myinterface interface{
	Func1()  // 无参无返回值
	Func2(a int)  // 有参无返回值
	Func3() (string,int)  // 无参有返回值
	Func4(a string, b int) (bool)  // 有参有返回值
}

type Dog struct {
	Name string
}

func (d *Dog) Func1(){}

func (d *Dog) Func2(a int){}

func (d *Dog) Func3() (string, int){
	return "hello", 100
}

func (d *Dog) Func4(a string, b int) (bool){
	return true
}


func main(){
	d := new(Dog)
	d.Name = "旺财"
	var i myinterface
	i = d
	fmt.Println(i)
	//&{旺财}
}
接口实现示例

 类型断言

switch 接口变量.(type) {
    case 类型1:
        // 变量是类型1时的处理
    case 类型2:
        // 变量是类型2时的处理
    …
    default:
        // 变量不是所有case中列举的类型时的处理
}

- 接口变量:表示需要判断的接口类型的变量。
- 类型1、类型2……:表示接口变量可能具有的类型列表,满足时,会指定 case 对应的分支进行处理。类型可以是基本类型、自定义的struct、自定义的interface

接口和类型之间的转换

语法:
  接口.(转换后的类型)

在知道接口类型的情况下:   t,ok := i.(T) - i 代表接口变量 - T 代表转换的目标类型 - t 代表转换后的变量。 - ok i接口是否实现T类型的结果 接口.(int) 接口.(string) ...

 

接口的使用

空接口

赋值

package main

import "fmt"

// 可以给空接口起名字
//type EmptyInterface interface {}

func main() {
	var a interface{}

	a = 1
	fmt.Printf("value: %v, type: %T\n",a,a)

	a = "hello world"
	fmt.Printf("value: %v, type: %T\n",a,a)

	a = true
	fmt.Printf("value: %v, type: %T\n",a,a)

	a = map[int]string{}
	fmt.Printf("value: %v, type: %T\n",a,a)

	a = []string{}
	fmt.Printf("value: %v, type: %T\n",a,a)

	//value: 1, type: int
	//value: hello world, type: string
	//value: true, type: bool
	//value: map[], type: map[int]string
	//value: [], type: []string
}
空接口可被赋值为所有类型的数据

类型判断

package main

import "fmt"

type testStruct struct {

}

func TestAssertion(i interface{}){
	switch i.(type) {
	case int,int8,int16,int32,int64:
		fmt.Printf("int类型 value:%d\n",i)
	case float32,float64:
		fmt.Printf("float类型 value:%f\n",i)
	case string:
		fmt.Printf("string类型 value:%s\n",i)
	case bool:
		fmt.Printf("bool类型 value:%t\n",i)
	case testStruct:
		fmt.Printf("testStruce类型 value:%v\n",i)
	default:
		fmt.Printf("default:%T类型 value:%v\n",i,i)
	}
}

func main() {
	TestAssertion(1)
	TestAssertion(1.1)
	TestAssertion("hello world")
	TestAssertion(testStruct{})
	TestAssertion(&testStruct{})
	TestAssertion([]int{})
	//int类型 value:1
	//float类型 value:1.100000
	//string类型 value:hello world
	//testStruce类型 value:{}
	//default:*main.testStruct类型 value:&{}
	//default:[]int类型 value:[]
}
类型判断,语法是:接口.(type)且必须使用在switch里

获取值

package main

import "fmt"

func main() {
	// 声明a变量, 类型int, 初始值为1
	var a int = 1
	// 声明i变量, 类型为interface{}, 初始值为a, 此时i的值变为1
	var i interface{} = a
	// 声明b变量, 尝试赋值i
	//报错
	//var b int = i

	var b int = i.(int)
	fmt.Printf("value: %v, type: %T", b, b)
	//value: 1, type: int
}
接口不能直接赋值给别的变量,需要对接口进行类型断言,语法是:接口.(数据类型)

接口比较

- 类型不同的空接口间的比较结果不相同

- 不能比较空接口中的动态值
类型的可比较性
类  型说  明
map 宕机错误,不可比较
切片([]T) 宕机错误,不可比较
通道(channel) 可比较,必须由同一个 make 生成,也就是同一个通道才会是 true,否则为 false
数组([容量]T) 可比较,编译期知道两个数组是否一致
结构体 可比较,可以逐个比较结构体的值
函数 可比较

 

package main

import "fmt"

type S struct {}

func main() {
	// a保存整型
	var a interface{} = 100
	// b保存字符串
	var b interface{} = "hi"
	// 两个空接口不相等
	fmt.Println(a == b)
	//false

	s := S{}
	fmt.Println(a == s)
	//false


	// c保存包含10的整型切片
	//var c interface{} = []int{10}
	// d保存包含20的整型切片
	//var d interface{} = []int{20}
	// 这里会发生崩溃
	//fmt.Println(c == d)
	//panic: runtime error: comparing uncomparable type []int
}
接口比较 true/false 或 comparing uncomparable type

类型实现接口

package main

import "fmt"

type Interface1 interface {
	Run()
}

type Interface2 interface {
	Walk()
}

type Dog struct {
	Name string
}

func (d *Dog) Run() {}

func (d *Dog) Walk() {}

func main() {
	d := &Dog{
		Name: "旺财",
	}
	var i1 Interface1
	var i2 Interface2
	//Dog实现了Run方法,因此可以复制给i1
	i1 = d
	//Dog实现了Walk方法,因此可以复制给i2
	i2 = d

	fmt.Println(i1)
	//&{旺财}

	fmt.Println(i2)
	//&{旺财}
}
一对多:一个类型实现多个接口
package main

import "fmt"

type Interface1 interface {
	Run()
}

type Dog struct {
	Name string
}

func (d *Dog) Run() {}

type Car struct {
	Name  string
	Price float64
}

func (c *Car) Run(){
	fmt.Println("法拉利飙车200 mile/h")
}


func main() {
	//Dog实现了Run
	d := &Dog{
		Name: "旺财",
	}
	//Car实现了Run
	c := &Car{
		Name:  "法拉利",
		Price: 11.11,
	}

	var i1 Interface1
	i1 = d
	fmt.Println(i1)

	i1 = c
	fmt.Println(i1)
}
多对一:多个类型实现同一接口

接口的嵌套组合

package main

import "fmt"

type Talker interface {
	Talk()
}

type Walker interface {
	Walk()
}

type Swimmer interface {
	Swim()
}

type Flyer interface{
	Fly()
}


type WTer interface {
	Walker
	Talker
}

type STer interface {
	Swimmer
	Talker
}

type FTer interface{
	Flyer
	Talker
}


type Dog struct {
	Name string
}

func (d *Dog) Talk() {}

func (d *Dog) Walk() {}

type Bird struct {
	Name string
}

func (b *Bird) Talk() {}

func (b *Bird) Fly() {}

type Fish struct {
	Name string
}

func (f *Fish) Talk() {}

func (f *Fish) Swim() {}


func main() {
	d := &Dog{Name: "旺财",}
	f := &Fish{Name:"海豚",}
	b := &Bird{Name:"鹰",}

	var wt WTer
	var st STer
	var ft FTer

	wt = d
	st = f
	ft = b

	fmt.Println(wt)
	fmt.Println(st)
	fmt.Println(ft)
	//&{旺财}
	//&{海豚}
	//&{鹰}
}
接口之间的嵌套 注意:接口下实现的方法不能有重复,嵌套时需注意

 排序(借助sort.Interface接口)

自定义sort.Interface接口

排序是常见的算法之一,也是常见的面试题之一,程序员对各种排序算法也是津津乐道。实际使用中,语言的类库会为我们提供健壮、高性能的排序算法库。

 接口源码
// sort.Sort使用 func Sort(data Interface) // sort.Interface接口源码 type Interface interface { // Len is the number of elements in the collection. Len() int // Less reports whether the element with // index i should sort before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) }
package main

import (
	"fmt"
	"math/rand"
	"sort"
)

type Student struct {
	Name string
	Age int
	Score int
}

type StudentList []*Student

func (s StudentList) Len() int {
	return len(s)
}

func (s StudentList) Less(i, j int) bool {
	return s[i].Score < s[j].Score
}

func (s StudentList) Swap(i, j int){
	s[i],s[j] = s[j],s[i]
}


func slist_init(sl *StudentList){
	for i := 0; i < 10; i++ {
		name := fmt.Sprintf("stu%d",i)
		age := rand.Intn(18)
		score := rand.Intn(100)
		*sl = append(*sl,&Student{Name:name,Age:age,Score:score})
	}
}

func main() {
	sl := StudentList{}
	slist_init(&sl)
	for _,s := range sl {
		fmt.Println(s)
	}

	fmt.Println("----------- sort start -----------")
	sort.Sort(sl)
	for _,s := range sl {
		fmt.Println(s)
	}
	fmt.Println("----------- sort end -----------")
	//&{stu0 5 87}
	//&{stu1 11 59}
	//&{stu2 13 18}
	//&{stu3 7 40}
	//&{stu4 4 0}
	//&{stu5 14 11}
	//&{stu6 12 89}
	//&{stu7 4 74}
	//&{stu8 9 45}
	//&{stu9 11 6}
	//----------- sort start -----------
	//	&{stu4 4 0}
	//&{stu9 11 6}
	//&{stu5 14 11}
	//&{stu2 13 18}
	//&{stu3 7 40}
	//&{stu8 9 45}
	//&{stu1 11 59}
	//&{stu7 4 74}
	//&{stu0 5 87}
	//&{stu6 12 89}
	//----------- sort end -----------


}
sort使用例子,根据struct对象下的某一field进行比较排序
package main
import (
    "fmt"
    "sort"
)
// 声明英雄的分类
type HeroKind int
// 定义HeroKind常量, 类似于枚举
const (
    None HeroKind = iota
    Tank
    Assassin
    Mage
)
// 定义英雄名单的结构
type Hero struct {
    Name string  // 英雄的名字
    Kind HeroKind  // 英雄的种类
}
// 将英雄指针的切片定义为Heros类型
type Heros []*Hero
// 实现sort.Interface接口取元素数量方法
func (s Heros) Len() int {
    return len(s)
}
// 实现sort.Interface接口比较元素方法
func (s Heros) Less(i, j int) bool {
    // 如果英雄的分类不一致时, 优先对分类进行排序
    if s[i].Kind != s[j].Kind {
        return s[i].Kind < s[j].Kind
    }
    // 默认按英雄名字字符升序排列
    return s[i].Name < s[j].Name
}
// 实现sort.Interface接口交换元素方法
func (s Heros) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
func main() {
    // 准备英雄列表
    heros := Heros{
        &Hero{"吕布", Tank},
        &Hero{"李白", Assassin},
        &Hero{"妲己", Mage},
        &Hero{"貂蝉", Assassin},
        &Hero{"关羽", Tank},
        &Hero{"诸葛亮", Mage},
    }
    // 使用sort包进行排序
    sort.Sort(heros)
    // 遍历英雄列表打印排序结果
    for _, v := range heros {
        fmt.Printf("%+v\n", v)
    }
}
其他例子,Less对多个field进行先后比较

使用sort.Slice进行切片元素排序

从 Go 1.8 开始,Go 语言在 sort 包中提供了 sort.Slice() 函数进行更为简便的排序方法。sort.Slice() 函数只要求传入需要排序的数据,以及一个排序时对元素的回调函数,类型为 func(i,j int)bool
sort.Slice()函数定义如下:

func Slice(slice interface{}, less func(i, j int) bool)
package main
import (
	"fmt"
	"sort"
)
type HeroKind int
const (
	None = iota
	Tank
	Assassin
	Mage
)
type Hero struct {
	Name string
	Kind HeroKind
}
func main() {
	heros := []*Hero{
		{"吕布", Tank},
		{"李白", Assassin},
		{"妲己", Mage},
		{"貂蝉", Assassin},
		{"关羽", Tank},
		{"诸葛亮", Mage},
	}
	sort.Slice(heros, func(i, j int) bool {
		// 先比较kind
		if heros[i].Kind != heros[j].Kind {
			return heros[i].Kind < heros[j].Kind
		}
		// 再比较Name
		return heros[i].Name < heros[j].Name
	})
	for _, v := range heros {
		fmt.Printf("%+v\n", v)
	}
}
sort.Slice使用举例

 

常用内建排序接口

sort 包中内建的类型排序接口
类  型实现 sort.lnterface 的类型直接排序方法说  明
字符串(String) StringSlice sort.Strings(a [] string) 字符 ASCII 值升序
整型(int) IntSlice sort.Ints(a []int) 数值升序
双精度浮点(float64) Float64Slice sort.Float64s(a []float64) 数值升序
源码举例说明

type StringSlice []string

// StringSlice实现了Len/Less/Swap,可调用sort.Sort
func (p StringSlice) Len() int           { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }


// 封装到Strings方法里,把[]string转为StringSlice
func Strings(a []string) { Sort(StringSlice(a)) }
package main

import (
	"fmt"
	"sort"
)


func main(){
	names := sort.StringSlice{
		"3. Triple Kill",
		"5. Penta Kill",
		"2. Double Kill",
		"4. Quadra Kill",
		"1. First Blood",
	}
	//sort.Sort(names)
	names.Sort()

	for _, v := range names {
		fmt.Printf("%s\n", v)
	}

	fmt.Println()
	names = []string{
		"3. Triple Kill",
		"5. Penta Kill",
		"2. Double Kill",
		"4. Quadra Kill",
		"1. First Blood",
	}
	sort.Strings(names)
	// 遍历打印结果
	for _, v := range names {
		fmt.Printf("%s\n", v)
	}
}
sort内建接口举例 sort.strings和StringSlice

 一些使用的例子

实现日志系统(支持多种输出方式)

- logwriter.go,实现一个Logger的struct,里面是一个由符合LogWriter指针的对象组成的slice,这些对象需要实现Write这个方法。

- consolewriter.go 实现一个consoleWriter的struct,该对象实现Write方法

- filesolewriter.go 实现一个filewriter的struct,该对象实现Write方法

- main.go 主逻辑,生成一个Logger,插入上面两个writer,Log方法把所有writer进行Write操作
package main
import (
	"fmt"
	"os"
)
// 命令行写入器
type consoleWriter struct {
}
// 实现LogWriter的Write()方法
func (f *consoleWriter) Write(data interface{}) error {
	// 将数据序列化为字符串
	str := fmt.Sprintf("%v\n", data)
	// 将数据以字节数组写入命令行中
	_, err := os.Stdout.Write([]byte(str))
	return err
}
// 创建命令行写入器实例
func newConsoleWriter() *consoleWriter {
	return &consoleWriter{}
}
consolewriter.go
package main
import (
	"errors"
	"fmt"
	"os"
)
// 声明文件写入器
type fileWriter struct {
	file *os.File
}
// 设置文件写入器写入的文件名
func (f *fileWriter) SetFile(filename string) (err error) {
	// 如果文件已经打开, 关闭前一个文件
	if f.file != nil {
		f.file.Close()
	}
	// 创建一个文件并保存文件句柄
	f.file, err = os.Create(filename)
	// 如果创建的过程出现错误, 则返回错误
	return err
}
// 实现LogWriter的Write()方法
func (f *fileWriter) Write(data interface{}) error {
	// 日志文件可能没有创建成功
	if f.file == nil {
		// 日志文件没有准备好
		return errors.New("file not created")
	}
	// 将数据序列化为字符串
	str := fmt.Sprintf("%v\n", data)
	// 将数据以字节数组写入文件中
	_, err := f.file.Write([]byte(str))
	return err
}
// 创建文件写入器实例
func newFileWriter() *fileWriter {
	return &fileWriter{}
}
filewriter.go
package main
import (
	"errors"
	"fmt"
	"os"
)
// 声明文件写入器
type fileWriter struct {
	file *os.File
}
// 设置文件写入器写入的文件名
func (f *fileWriter) SetFile(filename string) (err error) {
	// 如果文件已经打开, 关闭前一个文件
	if f.file != nil {
		f.file.Close()
	}
	// 创建一个文件并保存文件句柄
	f.file, err = os.Create(filename)
	// 如果创建的过程出现错误, 则返回错误
	return err
}
// 实现LogWriter的Write()方法
func (f *fileWriter) Write(data interface{}) error {
	// 日志文件可能没有创建成功
	if f.file == nil {
		// 日志文件没有准备好
		return errors.New("file not created")
	}
	// 将数据序列化为字符串
	str := fmt.Sprintf("%v\n", data)
	// 将数据以字节数组写入文件中
	_, err := f.file.Write([]byte(str))
	return err
}
// 创建文件写入器实例
func newFileWriter() *fileWriter {
	return &fileWriter{}
}
logwriter.go
package main
import "fmt"
// 创建日志器
func createLogger() *Logger {
	// 创建日志器
	l := NewLogger()
	// 创建命令行写入器
	cw := newConsoleWriter()
	// 注册命令行写入器到日志器中
	l.RegisterWriter(cw)
	// 创建文件写入器
	fw := newFileWriter()
	// 设置文件名
	if err := fw.SetFile("log.log"); err != nil {
		fmt.Println(err)
	}
	// 注册文件写入器到日志器中
	l.RegisterWriter(fw)
	return l
}
func main() {
	// 准备日志器
	l := createLogger()
	// 写一个日志
	l.Log("hello")
}
main.go

 

使用空接口实现可以保存任意值的字典

package main
import "fmt"
// 字典结构
type Dictionary struct {
	data map[interface{}]interface{} // 键值都为interface{}类型
}
// 根据键获取值
func (d *Dictionary) Get(key interface{}) interface{} {
	return d.data[key]
}
// 设置键值
func (d *Dictionary) Set(key interface{}, value interface{}) {
	d.data[key] = value
}
// 遍历所有的键值,如果回调返回值为false,停止遍历
func (d *Dictionary) Visit(callback func(k, v interface{}) bool) {
	if callback == nil {
		return
	}
	for k, v := range d.data {
		if !callback(k, v) {
			return
		}
	}
}
// 清空所有的数据
func (d *Dictionary) Clear() {
	d.data = make(map[interface{}]interface{})
}
// 创建一个字典
func NewDictionary() *Dictionary {
	d := &Dictionary{}
	// 初始化map
	d.Clear()
	return d
}
func main() {
	// 创建字典实例
	dict := NewDictionary()
	// 添加游戏数据
	dict.Set("My Factory", 60)
	dict.Set("Terra Craft", 36)
	dict.Set("Don't Hungry", 24)
	// 获取值及打印值
	favorite := dict.Get("Terra Craft")
	fmt.Println("favorite:", favorite)
	// 遍历所有的字典元素
	dict.Visit(func(key, value interface{}) bool {
		// 将值转为int类型,并判断是否大于40
		if value.(int) > 40 {
			// 输出很贵
			fmt.Println(key, "is expensive")
			return true
		}
		// 默认都是输出很便宜
		fmt.Println(key, "is cheap")
		return true
	})
}

//favorite: 36
//Don't Hungry is cheap
//My Factory is expensive
//Terra Craft is cheap
使用空接口实现可以保存任意值的字典

 

参考or转发

http://c.biancheng.net/golang/interface/

posted @ 2019-01-24 21:46  fat39  阅读(234)  评论(0)    收藏  举报