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:[]
}
获取值
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
}
类型实现接口
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 -----------
}
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)
}
}
使用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.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)
}
}
一些使用的例子
实现日志系统(支持多种输出方式)
- 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{}
}
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{}
}
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{}
}
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")
}
使用空接口实现可以保存任意值的字典
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

浙公网安备 33010602011771号