接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
Go 语言的接口设计是非侵入式的,接口编写者无须知道接口被哪些类型实现。而接口实现者只需知道实现的是什么样子的接口,但无须指明实现哪一个接口。编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。
Go语言中使用接口来体现多态,是duck-type(鸭子类型)的一种体现。当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
接口的定义与实现
package main
import (
"fmt"
)
// 定义一个接口
type Phone interface {
call()
}
// 定义一个结构体
type NokiaPhone struct {
}
// 实现
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
//定义接口类型的变量
var phone Phone
//只要实现了此接口方法的类型,那么这个类型的变量(接收者类型)就可以给i赋值
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
package main
import "fmt"
// 声明一个接口
type reg interface {
get(str string) string
}
// 声明结构体
type str1 struct {
str string
}
// 定义结构体方法
func (s str1) get(str string) string {
return str + "str1"
}
type str2 struct {
str string
}
func (s str2) get(str string) string {
return str + "str2"
}
func getStr(r reg) {
fmt.Println(r.get("你是猪!"))
}
func main() {
// 实现了方法 就是实现了接口
testStr := str2{"11111111111"}
getStr(testStr)
}
接口被实现的条件
接口的方法与实现接口的类型方法格式一致,在类型中添加与接口签名一致的方法就可以实现该方法。签名包括方法中的名称、参数列表、返回参数列表。也就是说,只要实现接口类型中的方法的名称、参数列表、返回参数列表中的任意一项与接口要实现的方法不一致,那么接口的这个方法就不会被实现。
接口中所有方法均被实现,当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用。
空接口与类型断言
具有0个方法的接口称为空接口。它表示为interface {}。由于空接口有0个方法,所有类型都实现了空接口。
类型断言用于提取接口的基础值,语法:i.(T)
package main
import(
"fmt"
)
func assert(i interface{}){
s:= i.(int)
fmt.Println(s)
}
// 程序打印的是int值, 但是如果我们给s 变量赋值的是string类型,程序就会panic。
func main(){
var s interface{} = 55
assert(s)
}
// 可以这样写 =======================================================
package main
import (
"fmt"
)
func assert(i interface{}) {
v, ok := i.(int)
fmt.Println(v, ok)
}
func main() {
// 如果 i 的值是int类型, 那么v就是i 对应的值,
// ok就是true。否则ok为false,程序并不会panic。
var s interface{} = 56
assert(s)
var i interface{} = "Steven Paul"
assert(i)
}
类型判断
类型判断的语法类似于类型断言。在类型断言的语法i.(type)中,类型type应该由类型转换的关键字type替换。
// 基本类型判断
package main
import (
"fmt"
)
func findType(i interface{}) {
switch i.(type) {
case string:
fmt.Printf("String: %s\n", i.(string))
case int:
fmt.Printf("Int: %d\n", i.(int))
default:
fmt.Printf("Unknown type\n")
}
}
func main() {
findType("Naveen")
findType(77)
findType(89.98)
}
/*
=================================================================
还可以将类型与接口进行比较。如果我们有一个类型并且
该类型实现了一个接口,那么可以将它与它实现的接口进行比较。
*/
package main
import "fmt"
type Describer interface {
Describe()
}
type Person struct {
name string
age int
}
func (p Person) Describe() {
fmt.Printf("%s is %d years old", p.name, p.age)
}
func findType(i interface{}) {
switch v := i.(type) {
case Describer:
v.Describe()
default:
fmt.Printf("unknown type\n")
}
}
func main() {
findType("Naveen")
p := Person{
name: "Naveen R",
age: 25,
}
findType(p)
}
// 输出
unknown type
Naveen R is 25 years old // 验证成功 调用 Describe()
接口的继承
接口既然可以继承自然可以组合
package main
import "fmt"
// 定义一个接口 声明SayHi方法
type Humaner interface {
SayHi()
}
// 定义一个接口 继承Humaner接口 存在 SayHi、Sing方法
type Personer interface {
Humaner
Sing(lrc string)
}
// 定义一个结构体
type Student struct {
name string
id int
}
// 实现接口的sayhi
func (s *Student)SayHi() {
fmt.Printf("%s sayhi\n", s.name)
}
// 实现接 Sing
func (p *Student)Sing(lrc string) {
fmt.Printf("student %s sing %s\n", p.name, lrc)
}
func main() {
//定义一个接口的类型的变量
var i Personer
s := &Student{"mike", 1}
i = s
i.SayHi()
i.Sing("loving you ")
}
// 执行结果
// mike sayhi
// student mike sing loving you