接口是一组方法签名的集合,然后我们可以定义一个结构体struct来实现该接口的所有方法,这样来看,接口就定义了对象的行为。
对于接口大家都有着一定的了解,需要注意的就是实现接口,那就必须实现它里面的所有方法。
我们定义一个形状Shape的接口,里面声明了周长与面积的两个方法,然后定义矩形Rectangle与圆形Circular的结构体类型,通过这个类型实现这些方法,由于方法都已在接口里面声明,所以矩形与圆形的类型也就实现了形状Shape接口。
- package main
-
- import (
- "fmt"
- "math"
- )
-
- // 形状接口,声明周长与面积方法
- type Shape interface {
- Perimeter() float64
- Area() float64
- }
-
- // 矩形结构体:宽度与高度
- type Rectangle struct {
- Width float64
- Height float64
- }
-
- // 圆结构体:半径
- type Circular struct {
- Radius float64
- }
-
- // Rectangle实现周长的方法
- func (R Rectangle) Perimeter() float64 {
- return (R.Width + R.Height) * 2
- }
-
- // Rectangle实现面积的方法
- func (R Rectangle) Area() float64 {
- return R.Width * R.Height
- }
-
- // Circular实现周长的方法
- func (C Circular) Perimeter() float64 {
- return math.Pi * (C.Radius * 2)
- }
-
- // Circular实现面积的方法
- func (C Circular) Area() float64 {
- return math.Pi * (C.Radius * C.Radius)
- }
- func main() {
- var shape Shape = Rectangle{4.5, 5}
- fmt.Printf("%T,%#v,%#v\n", shape, shape.Perimeter(), shape.Area()) //main.Rectangle,19,22.5
-
- var shape2 Shape = Circular{6}
- fmt.Printf("%T,%.2f,%.2f", shape2, shape2.Perimeter(), shape2.Area())//main.Circular,37.70,113.10
- }
从结果也看出这个类型是可以赋值给接口,赋值的类型不一样,其接口类型也跟着发生变化。就是说什么类型的赋值,接口就会成为这个具体的类型。
空接口也就是说里面没有声明方法,啥都没有,我们来看下空接口作为方法参数的一个例子:
- package main
-
- import (
- "fmt"
- )
-
- type GetInfo string
-
- type Person struct {
- Name string
- Age int
- }
-
- // 空接口做参数
- func test(i interface{}) {
- fmt.Printf("%T,%#v\n", i, i)
- }
- func main() {
- getinfo := GetInfo("寅恪光潜")
- person := Person{"Tony", 18}
- test(getinfo) //main.GetInfo,"寅恪光潜"
- test(person) //main.Person,main.Person{Name:"Tony", Age:18}
- }
我们可以看出test方法里面的参数是空接口,可以接收任意类型的参数,因为所有类型本已都实现了空接口。
结果也正确显示了参数进来的类型与值,再次证明了上面接口的类型也是随着具体类型变化而变化的。
想要实现多接口,其实跟实现一个接口类似,只不过多个接口里的方法都一一实现,既然方法都已实现,这样也就实现了多接口。
我们定义两个形状接口,一个包含计算面积的方法,另一个包含计算体积的方法。然后我们分别将正方体类型赋值给这两个接口
- package main
-
- import "fmt"
-
- type Shape1 interface {
- Area() float64
- }
- type Shape2 interface {
- Volume() float64
- }
-
- // 正方体结构体:边长
- type Cube struct {
- Edge float64
- }
-
- // 面积
- func (C Cube) Area() float64 {
- return 6 * C.Edge * C.Edge
- }
-
- // 体积
- func (C Cube) Volume() float64 {
- return C.Edge * C.Edge * C.Edge
- }
-
- func main() {
- C := Cube{4}
- var S1 Shape1 = C
- var S2 Shape2 = C
- fmt.Printf("%#v\n", S1.Area()) //96
- fmt.Printf("%#v", S2.Volume()) //64
- }
因为正方体Cube实现了面积与体积的方法,也就是这两个接口里的方法都实现了,所以就实现了两个接口。
如果S1.Area() 换成 S1.Volume()会报错:
Build Error: go build -o c:\Users\Tony\__debug_bin.exe -gcflags all=-N -l .
# _/c_/Users/Tony
.\test.go:30:25: S1.Volume undefined (type Shape1 has no field or method Volume) (exit status 2)
因为这个S1接口里面是面积的方法,没有体积的方法,所以编译的时候就会报错。
那既然是实现了多接口,那想要调用另外接口的方法,该如何处理呢?
将用到类型的断言。类型断言的语法为:i.(Type)
- func main() {
- var S1 Shape1 = Cube{4}
- c := S1.(Cube)
- fmt.Println(c.Area()) //96
- fmt.Println(c.Volume()) //64
- }
这样就可以分别去调用两个接口里面的方法了。
当然了,这个类型是需要实现接口的,如果没有实现会报错,所以最好是有一种判断来处理。
- s1, ok := S1.(Cube)
- fmt.Println(s1, ok)//{4} true
通过这个ok值来判断,为true就是实现了这个接口。
- type Tony interface {
- Sing() string
- }
- s2, ok2 := S1.(Tony)
- fmt.Println(s2, ok2)//
false
由于Cube没有实现Sing方法,也就是没有实现Tony接口,所以第二个返回值就是false
通过上面的学习,我们又回到上面的空接口的示例,针对输入的类型分别做判断:
代码如下:
- package main
-
- import (
- "fmt"
- "strings"
- )
-
- type Person struct {
- Name string
- Age int
- }
-
- // 空接口参数
- func test(i interface{}) {
- switch i.(type) {
- case string:
- fmt.Printf("%#v\n", strings.ToUpper(i.(string)))
- case int:
- fmt.Printf("%#v\n", i)
- default:
- fmt.Printf("%T,%v", i, i)
- }
- }
- func main() {
- person := Person{"Tony", 18}
- test("hello")
- test(110)
- test(person)
- }
- /*
- "HELLO"
- 110
- main.Person,{Tony 18}
- */