在Golang中一个接口由两个部分组成,分别是动态类型和动态值。在一个时刻,一个接口只能有一个类型和值,这是这个接口该时刻的具体类型和具体值。因为一个接口可以有多个结构体实现,所以当不同的实现该接口的结构体赋值给一个接口变量的时候,接口的动态类型会变成该结构体类型,接口内部存储的指向结构体的指针会指向当前赋值的结构体,而动态值会变成当前赋值的结构体的值。
例如
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
上述代码中 Circle结构体和Rectangle 结构体分别实现了Shape接口
func main(){
var s Shape
println("第一次赋值")
s = Circle{4.1}
println("第二次赋值")
s = Rectangle{Width: 3.3, Height: 4.4}
}
然后我们分别为其两次赋值,第一次赋值的时候动态类型为main.Circle,动态值为{4.1};第二次赋值动态类型为main.Rectangle,动态值为{3.3,4.4}。
赋值之后因为s类型为Shape,我们无法调用其动态类型变量的动态值如无法通过
var shape Shape
s = Circle{4.1}
s.Radius
,这时候就需要用到断言来判断并将接口变量转换为具体的类型。
示例代码如下:
package main
import (
"fmt"
"math"
)
func main() {
var s Shape
println("第一次赋值")
s = Circle{4.1}
v, ok := s.(Circle)
if ok {
v1 := v.Radius
fmt.Printf("s的动态类型为%T\n这取出s的半径为%f\n", v, v1)
fmt.Println("动态值为", v)
fmt.Println("面积为", v.Area())
}
println("第二次赋值")
s = Rectangle{Width: 3.3, Height: 4.4}
va, ok := s.(Rectangle)
if ok {
v1 := va.Width
v2 := va.Height
fmt.Printf("s的动态类型为%T\n宽为%f\n高未%f\n", va, v1, v2)
fmt.Println("动态值为", va)
fmt.Println("面积为", va.Area())
}
}
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}