本节重点:
- 学会 Go 中 interface 的基本使用
在 Go 中,接口类型是一种抽象类型,是方法的集合,其他类型实现了这些方法就是实现了这个接口。
声明和实现接口
在 Go 中接口的声明如下:
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
简单示例
现在我们通过一个简单的示例来看是如何创建接口并实现它:
package main
import (
"fmt"
)
//interface definition
type VowelsFinder interface {
FindVowels() []rune
}
type MyString string
//MyString implements VowelsFinder
func (ms MyString) FindVowels() []rune {
var vowels []rune
for _, rune := range ms {
if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
vowels = append(vowels, rune)
}
}
return vowels
}
func main() {
name := MyString("Sam Anderson")
var v VowelsFinder
v = name // possible since MyString implements VowelsFinder
fmt.Printf("Vowels are %c", v.FindVowels())
}
在上面程序第8行中,创建了一个名为 VowelsFinder
的接口类型,它有一个方法 FindVowels() []rune
。
在下一行中创建一个类型 MyString
它只是 string
的包装类。
在第15行中,我们将方法 FindVowels()[]rune
添加到接收方类型 MyStrin
g 中。现在 MyString
被认为实现了 VowelsFinder
接口。这与 Java 等其他语言非常不同,在 Java 中,类必须使用 implements
关键字显式地声明它实现了接口。如果类型包含接口中声明的所有方法,则 go 和 go 接口将隐式实现。
在第28行,我们将 MyString
类型的 name
赋给 v
类型的 VowelsFinder
。这是可能的,因为 MyString
实现了 VowelsFinder
。下一行调用 MyString
类型上的 FindVowels
方法,并打印字符串中所有的元音 Sam Anderson
。这个程序输出的 Vowels are [a e o]
这样已经创建并实现了第一个接口。
空接口
一个没有方法的接口称为空接口。它表示为 interface{}
。由于空接口没有任何方法,所以所有类型都实现空接口。
package main
import (
"fmt"
)
func describe(i interface{}) {
fmt.Printf("Type = %T, value = %v\n", i, i)
}
func main() {
s := "Hello World"
describe(s)
i := 55
describe(i)
strt := struct {
name string
}{
name: "Naveen R",
}
describe(strt)
}
在上面的程序中第 7 行,describe(i interface{})
函数接受一个空接口作为参数,因此它可以传递任何类型。
我们将字符串、int 和 结构体分别传递给第 13、15 和 21 行中的 describe
函数。这个程序打印
类型断言
类型断言用于提取接口的基础值。
i.(T)
是用于获取具体类型为T
的接口i
的底层值的语法。
一个程序值一千字😀。让我们为类型断言写一个。
package main
import (
"fmt"
)
func assert(i interface{}) {
s := i.(int) //get the underlying int value from i
fmt.Println(s)
}
func main() {
var s interface{} = 56
assert(s)
}
第12行中的 s
的具体类型是 int
。我们使用第8行中的 i.(int)
语法来获取 i
的底层 int
值。这个程序打印 56
。
如果上面程序中的具体类型不是 int
会发生什么?好吧,让我们来了解一下。
package main
import (
"fmt"
)
func assert(i interface{}) {
s := i.(int)
fmt.Println(s)
}
func main() {
var s interface{} = "Steven Paul"
assert(s)
}
在上面的程序中,我们将具体类型string
的s
传递给assert
函数,试图从中提取 int
值。这个程序会因为这个问题而报错panic: interface conversion: interface {} is string, not int
。
为了解决上述问题,我们可以使用语法
v, ok := i.(T)
如果 i
的具体类型是 T
,那么 v
的值就是 i
, ok
为 true
。
如果 i
的具体类型不是 T
,那么 ok
将是 false
,v
将是类型 T
的零值,程序不会报错。
package main
import (
"fmt"
)
func assert(i interface{}) {
v, ok := i.(int)
fmt.Println(v, ok)
}
func main() {
var s interface{} = 56
assert(s)
var i interface{} = "Steven Paul"
assert(i)
}
当Steven Paul
被传递给assert
函数时,ok
将为 false
,因为i
具体类型不是int
,并且v
为 0
值。该程序将打印:
56 true
0 false
注意:
- 上面所有示例接口都是使用值接收器实现的。也可以使用指针接收器来实现接口。但是会有一些微妙的区别?动手试试!