文章目录
- 接口
- 声明接口
- 实现接口的条件
- 理解类型和接口的关系
- 接口的嵌套组合---将多个接口放在一个接口内
- 在接口和类型之间转换
- 空接口类型---能保存所有值的类型
- 类型分支---批量判断空接口中变量的类型
接口
Go语言中使用组合实现对象特性的描述。
对外通过接口暴露能使用的特性。
Go语言的接口设计是非侵入式的,接口编写者无需知道接口被哪些类型实现。而接口实现者只需要知道实现的是什么样子的接口。
接口实现者不需要关系接口被怎样使用,调用者无需关心接口的实现细节。
概念:接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式、类型和结构。
声明接口
每个接口类型由数个方法组成,格式如下:
type 接口类型名 interface{
方法名1(参数列表1) 返回值列表1
方法名2(参数列表2) 返回值列表2
...
}
开发中常见接口及写法:
type Writer interface {
Write(p []byte)(n int,err error)
}
type Stringer interface{
String() string
}
实现接口的条件
1.接口的方法与实现接口的类型方法格式一致
2.接口中所有的方法均被实现。
package main
import (
"fmt"
)
//定义一个数据写入器
type DataWriter interface {
WriterData(data interface{}) error
}
//定义文件结构,用于实现DataWriter
type file struct{}
//实现DataWriter接口的WriteData()方法
func (d *file) WriterData(data interface{}) error {
//模拟写入数据
fmt.Println("WriteData:", data)
return nil
}
func main() {
//实例化file
f := new(file)
//声明一个DataWriter的接口
var writer DataWriter
//将接口赋值f,也就是*file类型
writer = f
//使用DataWriter接口进行数据写入
writer.WriterData("data")
}
输出:
如果函数名不一样会报错吗?
type DataWriter interface {
WriterData(data interface{}) error
}
//定义文件结构,用于实现DataWriter
type file struct{}
//实现DataWriter接口的WriteData()方法
func (d *file) WriteData(data interface{}) error {
//模拟写入数据
fmt.Println("WriteData:", data)
return nil
}
编译器会输出:
如果方法签名不一样呢?答案是也会报错
理解类型和接口的关系
1.一个类型可以实现多个接口
看一个例子:
package main
import (
"io"
)
//接口
type Writer interface {
Writer(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
type Socket struct{}
//实现接口
func (s *Socket) Writer(p []byte) (n int, err error) {
return 0, nil
}
func (s *Socket) Close() error {
return nil
}
//使用接口
func usingWriter(writer io.Writer) {
writer.Write(nil)
}
func usingCloser(closer io.Closer) {
closer.Close()
}
func main() {
//实例化Socket
s := new(Socket)
usingCloser(s)
usingCloser(s)
}
图示:
2.多个类型可以实现相同的接口
也就是说一个接口中的方法可以由多个不同类型实现。
接口的嵌套组合—将多个接口放在一个接口内
接口和接口之间也可以嵌套组合成新的接口,只要接口的所有方法被实现,则这个接口的所有嵌套接口的方法均可以被调用。
接口嵌套组合示例:
Go语言的io包中定义了写入器(Writer)、关闭器(Closer)、和写入关闭器(WriteClose)3个接口。
实现:
package main
import (
"io"
)
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
type WriteCloser interface {
Writer
Closer
}
//声明一个设备结构
type device struct {
}
//实现io.Writer的write()方法
func (d *device) Write(p []byte) (n int, err error) {
return 0, nil
}
//实现io.Closer的close方法
func (d *device) Close() error {
return nil
}
func main() {
//声明写入关闭器,并赋予device实例
var wc io.WriteCloser = new(device)
wc.Write(nil)
wc.Close()
var writeonly io.Writer = new(device)
writeonly.Write(nil)
}
在接口和类型之间转换
Go语言中使用接口断言将接口转换成另外一个接口,也可以将接口转换成为另外的类型。接口的转换在开发中非常常见,使用也非常的频繁。
类型断言的基本格式:
t :=i.(T)
//t表示转换后的变量
//T代表转换的目标类型
//i代表接口变量
//另一种写法。
t ,ok :=i.(T)
//这种写法,如果发生接口未实现时,将会把ok置为false,t置为T类型的0值。
接口可以转换为其他接口。
接口也可以转换为其他类型。
接口在转换为其他类型时,接口内保存的实例对应的指针,而且是要转换的对应的类型指针。
空接口类型—能保存所有值的类型
将值保存到空接口
package main
import (
"fmt"
)
//空接口
var any interface{}
func main() {
any = 1
fmt.Println(any)
any = "hello"
fmt.Println(any)
any = false
fmt.Println(any)
}
输出:
1
hello
false
从空接口获取值
var b bool = any.(bool)
fmt.Println(b)
输出:false
注意:多个不同类型的值连续赋给空接口,空接口会存储值。但是空接口的类型会进行覆盖,取值的话只能取最后放入的那个类型的值。
比如:如果上述例子中取int型的话就会宕机:
var b int = any.(int)
fmt.Println(b)
会出现这样的宕机内容:
panic: interface conversion: interface {} is bool, not int
空接口的值比较:用空接口保存值可以进行比较,如果两个不相等,返回false.
类型分支—批量判断空接口中变量的类型
类型分支断言的基本格式:
switch 接口变量.(type){
case 类型1:
case 类型2:
default:
}
示例:使用类型分支判断基本类型
package main
import (
"fmt"
)
func printType(v interface{}) {
switch v.(type) {
case int:
fmt.Println(v, "is int")
case string:
fmt.Println(v, "is string")
case bool:
fmt.Println(v, "is bool")
}
}
func main() {
printType(1024)
printType("pig")
printType(true)
}
输出:
1024 is int
pig is string
true is bool