接口与多态
何为接口
在面向对象的领域里,接口一般这样定义:接口定义一个对象的行为。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定。
在 Go 语言中,接口就是方法签名(Method Signature)的集合。当一个类型定义了接口中的所有方法,我们称它实现了该接口。接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法。
接口定义
使用type关键字来定义接口
如下所示,定义了一个叫order的接口,接口要求必须实现printName方法
type order interface{
printName()
}
接口的实现
如果有一个类型/结构体,实现了一个接口要求的所有方法,我们就可以称它实现了该接口。
接口实现多态
一个接口,在不同对象上的不同表现。这就是多态。在 Go 语言中,是通过接口来实现的多态。
package main
import (
"fmt"
"strconv"
)
//定义一个接口
type order interface{
countmoney() int
paymoney() string
}
type hamburger struct{
name string
quantity int
price int
}
func (ham hamburger) countmoney() (int){
return ham.quantity*ham.price
}
func (ham hamburger) paymoney() (string){
//strconv.Itoa() 可以将数字转换成对应的字符串类型的数字。
return "所购买的" + strconv.Itoa(ham.quantity) + "个"+ham.name+"共计:" + strconv.Itoa(ham.countmoney()) + "元"
}
type roastrice struct{
name string
quantity int
price int
}
func (roa roastrice) countmoney() (int){
return roa.quantity*roa.price
}
func (roa roastrice) paymoney() (string){
return "所购买的" + strconv.Itoa(roa.quantity) + "个"+roa.name+"共计:" + strconv.Itoa(roa.countmoney()) + "元"
}
func calaulateSum(orders []order)(int){
var sumPrice int
for _,now := range orders{
fmt.Println(now.paymoney())
sumPrice += now.countmoney()
}
return sumPrice
}
func main(){
ham := hamburger{
"烤鸡汉堡",
2,
18,
}
rice := roastrice{
"烤饭",
5,
2,
}
orders := []order{ham,rice}
sumPrice := calaulateSum(orders)
fmt.Printf("这顿饭共计%d元",sumPrice)
}
接口使用的注意事项
1.方法调用的限制
接口是一组固定的方法集,由于静态类型的限制,接口变量有时仅能调用其中特定的一些方法。
下面这段代码会报错
type Phone interface{
call()
}
type iPhone struct{
name string
}
func (ipone iPhone) call(){
fmt.Println("Hello iPhone.")
}
func (iphone iPhone) send_wechat(){
fmt.Println("Hello Wechat")
}
func main(){
var phone Phone
phone = iPhone{"xin's iphone"}
phone.call()
phone.send_wechat()
}
提示我们 Phone 类型的方法没有 send_wechat 的字段或方法。
原因也很明显,因为我们的phone对象显式声明为 Phone 接口类型,因此 phone调用的方法会受到此接口的限制。
修改方法:隐式实现
Phone 接口,如此一来,方法的调用就不会受到接口类型的约束。
/* var phone Phone
phone = iPhone{"xin's iphone"} */
phone := iPhone{"xin's iphone"}
phone.call()
phone.send_wechat()
2.调用函数时的隐式转换
Golang 语言中的函数调用都是值传递的,变量会在方法调用前进行类型转换。
eg:下面一段是可以正常运行的代码
func printTpe(i interface{}){
switch i.(type){
case int:
fmt.Println("参数的类型是 int")
case string:
fmt.Println("参数的类型是 string")
}
}
func main(){
a:=10
printTpe(a)
}
但是如果你把函数内的内容搬到到外面来
func main(){
a:=10
switch a.(type){
case int:
fmt.Println("参数的类型是 int")
case string:
fmt.Println("参数的类型是 string")
}
}
报错:
当一个函数接口 interface{} 空接口类型时,我们说它可以接收什么任意类型的参数(江湖上称之为无招胜有招)。就是把传入函数的参数值(注意:Go 语言中的函数调用都是值传递的)的类型隐式的转换成 interface{} 类型。
所以要想不通过接收空接口类型的函数进行类型判断,我们需要进行显示转换
func main(){
a:=10
switch interface{}(a).(type){
case int:
fmt.Println("参数的类型是 int")
case string:
fmt.Println("参数的类型是 string")
}
}