Go语言中接口interface(多接口,空接口,类别开关)的应用

接口是一组方法签名的集合,然后我们可以定义一个结构体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}
*/

你可能感兴趣的:(Golang,i.type,interface,多接口,空接口)