接口是一组方法的签名(这里説签名,因为真的只有名字,没有实现)的集合,
一个结构体可以选择实现接口的方法,通过接口,一个数据可以被多个不同的结构体使用。这就是多态
因此,接口就是定义了对象的行为。
For example, a Dog can walk and bark. If an interface defines method signatures for walk and bark while Dog implements walk and bark methods, then Dog is said to implement that interface.
接口定义方法的名字,参数,和返回值。
在GO中不需要使用 implement 接口
type Shape interface {
Area() float64
Perimeter() float64
}
这个接口定义了两个方法,任何实现了这两个方法的 type (如结构体)都实现了这个叫Shape 的接口
package main
import "fmt"
type Shape interface {
Area() float64
Perimeter() float64
}
func main() {
var s Shape
fmt.Println("value of s is", s)
fmt.Printf("type of s is %T\n", s)
}
上面代码会打印出0 和 nil.
这是因为我们定义了type Shape, 但并没有赋值
注意这个type!!!, 它既可以是结构体,也可以是接口
A variable of an interface type can hold a value of a type that implements the interface. The value of that type becomes the dynamic value of the interface and that type becomes the dynamic type of the interface.
当我们用println. 打印时,调用的是接口的动态值
package main
import "fmt"
type Shape interface {
Area() float64
Perimeter() float64
}
type Rect struct {
width float64
height float64
}
// 这里的结构体 实现了 Shape 接口
func (r Rect) Area() float64 {
return r.width * r.height
}
func (r Rect) Perimeter() float64 {
return 2 * (r.width + r.height)
}
func main() {
var s Shape
s = Rect{5.0, 4.0}
r := Rect{5.0, 4.0}
fmt.Printf("type of s is %T\n", s)
fmt.Printf("value of s is %v\n", s)
fmt.Println("area of rectange s", s.Area())
fmt.Println("s == r is", s == r)
}
// 结果如下
type of s is main.Rect
value of s is {5 4}
area of rectange s 20
s == r is true
这里的先创建接口Shape 和 结构体 Rect, 然后Rect实现了接口的方法 Area() Perimeter(),
这种实现都是** 自动发生的 。
下面就是有意思的事情了 :
一个type 实现了一个接口, 他本身的类型是不变的, 但是同时习得了这个接口的功能**。
比如张三学会了跳舞,他还是张三,但是他可以进行跳舞的活动了! 所以上面有 s == r .
这里的 type 是很灵活的,可以转换为其他的type , 包括struct 或 interface
没有方法的接口, 正是因为如此, 所有的type 都实现了这个接口
下面用fmt.Println() 来描述空接口的作用, 我们来看下这个函数的签名:
func Println(a …interface{}) (n int, err error)
可见他接受空接口作为参数
package main
import "fmt"
type MyString string
type Rect struct {
width float64
height float64
}
func explain(i interface{}) {
fmt.Printf("value given to explain function is of type '%T' with value %v\n", i, i)
}
func main() {
ms := MyString("Hello World!")
r := Rect{5.5, 4.5}
explain(ms)
explain(r)
}
知道为什么把 **i interface{}**做为参数吗,因为任务变量都 实现了空接口, 所以任何type都等于空接口!
package main
import "fmt"
type Shape interface {
Area() float64
}
type Object interface {
Volume() float64
}
type Cube struct {
side float64
}
func (c Cube) Area() float64 {
return 6 * (c.side * c.side)
}
func (c Cube) Volume() float64 {
return c.side * c.side * c.side
}
func main() {
c := Cube{3}
var s Shape = c
var o Object = c
fmt.Println("volume of s of interface type Shape is", s.Area())
fmt.Println("area of o of interface type Object is", o.Volume())
}
上面建立两个接口Shape, Object. 而结构体Cube同时实现了这两个接口, 则 var s Shape = c
var o Object = c 两个声明都是对的。
package main
import "fmt"
type Shape interface {
Area() float64
}
type Object interface {
Volume() float64
}
type Cube struct {
side float64
}
func (c Cube) Area() float64 {
return 6 * (c.side * c.side)
}
func (c Cube) Volume() float64 {
return c.side * c.side * c.side
}
func main() {
var s Shape = Cube{3}
c := s.(Cube)
fmt.Println("area of c of type Cube is", c.Area())
fmt.Println("volume of c of type Cube is", c.Volume())
}
这里 shape 类型, 的动态值是Cube 类型, 通过这个语法, 我们把Cube这个类型“提取”出来, c:=s.(Cube)
这样c 就可以使用Area() 和 Volume()方法了,
要注意的是, 使用语法 c := s.(Cube) 时, s中要有Cube的类型,如果Cube没有实现s接口的话,就会报错,
简单的説, s要“即是s,也是Cube”!
而如果没有给 s 添加 Cube 的值, 比如只有下面代码
var s Shape
c := s.(Cube)
则 ** c := s.(Cube)**会报错:
panic: interface conversion: main.Shape is nil, not main.Cube
为了避免这种错误,可以使用下面语法, 防止 panic
c, ok := s.(Cube)
我们还可以用类型断言的方式来检测一个type是否实现了某些接口:
如果断言一个type是不是接口,Golang就会检测他是否实现了该接口的方法
var c Cube= Cube{3}
check1 := c.(Object)
check12 := c.(Shape)
package main
import "fmt"
type Shape interface {
Area() float64
}
type Object interface {
Volume() float64
}
type Skin interface {
Color() float64
}
type Cube struct {
side float64
}
func (c Cube) Area() float64 {
return 6 * (c.side * c.side)
}
func (c Cube) Volume() float64 {
return c.side * c.side * c.side
}
func main() {
var s Shape = Cube{3}
value1, ok1 := s.(Object)
fmt.Printf("dynamic value of Shape 's' with value %v implements interface Object? %v\n", value1, ok1)
value2, ok2 := s.(Skin)
fmt.Printf("dynamic value of Shape 's' with value %v implements interface Skin? %v\n", value2, ok2)
}
总而言之,类型断言的作用有两个:
这里的type是保留字
这里的 i interface{} 表示任何类型传入的值
func explain(i interface{}) {
switch i.(type) {
case string:
fmt.Println("i stored string ", strings.ToUpper(i.(string)))
case int:
fmt.Println("i stored int", i)
default:
fmt.Println("i stored something else", i)
}
}
func main() {
explain("Hello World")
explain(52)
explain(true)
}
// 执行结果
i stored string HELLO WORLD
i stored int 52
i stored something else true
go 中接口不能实现其他接品, 也不能继承其他接口
但我们可以通过 合并 接口来实现类似的功能
package main
import "fmt"
type Shape interface {
Area() float64
}
type Object interface {
Volume() float64
}
type Material interface {
Shape
Object
}
type Cube struct {
side float64
}
func (c Cube) Area() float64 {
return 6 * (c.side * c.side)
}
func (c Cube) Volume() float64 {
return c.side * c.side * c.side
}
func main() {
c := Cube{3}
var m Material = c
var s Shape = c
var o Object = c
fmt.Printf("dynamic type and value of interface m of static type Material is'%T' and '%v'\n", m, m)
fmt.Printf("dynamic type and value of interface s of static type Shape is'%T' and '%v'\n", s, s)
fmt.Printf("dynamic type and value of interface o of static type Object is'%T' and '%v'\n", o, o)
}
上面代码,Cube结构体实现了 Area() , Volue() 方法,所以实现了 Shape 和 Object 接口。
同时因为 Material 接口是 Shape 和 Object 合并的, 所以 Cube 也实现了 Material 接口。
package main
import "fmt"
type Shape interface {
Area() float64
Perimeter() float64
}
type Rect struct {
width float64
height float64
}
func (r *Rect) Area() float64 {
return r.width * r.height
}
func (r Rect) Perimeter() float64 {
return 2 * (r.width + r.height)
}
func main() {
r := Rect{5.0, 4.0}
var s Shape = r
area := s.Area()
fmt.Println("area of rectangle is", area)
}
上面代码中, 函数Area 接收 *Rect , 所以接收器会得到一个指向type Area 的指针,但是运行会报编译错误
提示 Rect 没有实现 Shape接口的方法?
./prog.go:25:6: cannot use r (type Rect) as type Shape in assignment:
Rect does not implement Shape (Area method has pointer receiver)
问题出在这里:
r := Rect{5.0, 4.0}
// 下面这行会报错
var s Shape = r
r 没法传给 Shape
仔细看有这样的提示 : Area() 方法有一个指针接受者
也就是説实现 Area() 方法的不的 r , 而是 r 的指针, 就是 &r
上面Area() 方法的receiver 实际上是 *Rect
原因: 接口有点不同于函数,函数的 value receiver 和 pointer receier
深层原因:
一个method , 他的receiver 不管是值还是指针,都是固定的,值到指针或指针到值的转换也都是稳定的。
然而,对于 interface, 值和指针都是可变的。
要解决这个问题,要改用如下写法:
var s Shape = &r // assigned pointer
area := s.Area()
接口可以用 == 和 != 做比较,
var a, b interface{}
fmt.Println( a == b ) // true
对于非空接口,则他们的所有value 的值 和type都是相等的时候两个接口才相等
对于一些不可比较的值,如不同的数据,slice, function, map 等,比较则会抛出异常
如果一个interface 是 nil, 则 == 判断永远返回false