一、什么是接口?
编程
在面向对象的语言中,接口是用来限制实现类行为的。怎么理解这句话呢?设计模式
定义一个Person接口,我只会站在个人角度上考虑问题,好比Person(人),天然想到会吃饭、睡觉等:数组
interface Person编程语言 {ide // 人会吃饭函数 void eat();测试 // 人会睡觉ui void sleep();spa }设计 |
我是站在接口角度上考虑接口如何定义,此时不会过多考虑实现类的行为。
这很正常,由于我不能肯定谁会使用个人接口,有一天SuperMan说:“我要用你定义的接口”,那SuperMan必须用implements实现Person接口的行为:
// SuperMan实现Person接口 public class SuperMan implements Person { // 超人会吃饭 public void eat() { System.out.println("super man can eat."); } // 超人会睡觉 public void sleep() { System.out.println("super man can sleep."); } } |
等到SuperMan实现完了以后,他对我说:“做为超人,我是会飞的哦~”
这时做为Person定义者的我,只有两个选择:
对SuperMan说:“飞是你本身的行为,我不帮你定义这种行为!”。但是通过若干万年以后人类进化了怎么办?
对SuperMan说:“好吧,我帮你定义这种行为,但是一旦Person有了fly,你也必须实现fly”
其实不管上面哪一种结果,都至关于接口把实现类绑架了。
【备注】:悄悄地告诉你,上面的代码是Java语言
二、GO语言的接口呢?
GO语言有接口类型(interface{}),它与面向对象的接口含义不一样,GO语言的接口类型与数组(array)、切片(slice)、集合(map)、结构体(struct)是同等地位的。怎么理解这句话呢?
咱们前面已知道:
var num int // 定义了一个int型变量num |
同理:
var any interface{} // 定义了一个接口类型变量any |
从这个角度上看,GO的interface{}与面向对象的接口是不同吧。 更加不同的是,interface{}是一个任意类型,或者说是万能类型。
三、GO语言的任意类型
也就是说定义一个变量为interface{}类型,能够把任意的值赋给这个变量,例如:
var v1 interface{} = 250 // 把int值赋给interface{} var v2 interface{} = "eagle" // 把string值赋给interface{} var v3 interface{} = &v1 // 把v1的地址赋给interface{} |
固然函数的入参类型也能够是interface{},这样函数就能够接受任意类型的参数,例如GO语言标准库fmt中的函数Println()
func Println(args ...interface{}){ // 略 } |
任意类型看起来很爽,能够把任意的值都赋给interface{}类型变量,就像JDK1.4时的Vector,那时候Java尚未泛型的概念,任意值均可以向Vector里面放,但问题也接踵而至:
// 定义一个长度为3的Any类型数组,求数组元素之和,即anyArr[0]+anyArr[1]+anyArr[2] var anyArr [3]interface{} anyArr[0] = "eagle" // anyArr[0]赋值为字符串 anyArr[1] = 20 // anyArr[1]赋值为int anyArr[2] = 75.3 // anyArr[2]赋值为float64 // 此时若求和,会有什么结果呢? fmt.Println(anyArr[0] + anyArr[1] + anyArr[2]) |
四、类型判断
上例直观上来看,string不能和int直接相加,因此咱们须要判断元素类型,若元素类型是数字型的,咱们就执行“+”操做;若元素类型是字符串型的,咱们就跳过。这里须要引入另一个知识:switch-type
即:拿到一个interface{}以后,能够结合switch语句判断变量的类型
例如:
var v interface{} = 3 switch v.(type){ case int: fmt.Println("3 is int") case string: fmt.Println("3 is string") default: fmt.Println("unkown type") } |
因此上面的例子能够进一步修改以下:
// 定义一个长度为3的Any类型数组,求数组元素之和,即anyArr[0]+anyArr[1]+anyArr[2] var anyArr [3]interface{} anyArr[0] = "eagle" anyArr[1] = 20 anyArr[2] = 75.3 // 定义一个总和变量total var total float64 = 0 // 遍历Any类型数组 for i := 0; i < len(anyArr); i++ { // 针对Any类型数组中的每一个元素进行类型查询 switch vType := anyArr[i].(type) { case int: total += float64(vType) case float64: total += vType default: // do nothing } } // 打印Any类型数组中数字之和 fmt.Println(total) |
五、interface类型与struct类型
从上面看interface类型很简单嘛,或许吧。
咱们再回顾一下struct类型,struct类型是一个结构体,里面能够定义成员,它相似面向对象的一个类,类里面能够定义成员变量,好比:
// 定义一个person结构体,里面有姓名、年龄、身高、体重成员 type person struct{ name string age int height, weight float64 } |
那么interface类型是否也能够这样定义呢?以下:
/** * 定义一个手表接口,经过手表接口咱们能够知道小时、分钟和秒 */ type watch interface { getHour() int getMinute() int getSecond() int } |
经过编译(go build myIf.go)会发现并无抛出错误!
从结果能够看出彻底能够这样定义一个类型为interface的变量watch,而且还能够为watch增长相应的方法;但与struct不一样的是:struct里面的成员是变量,而interface里面的成员是函数,即咱们可使用interface定义接口。
六、interface定义接口示例
(1)GO语言接口实现
周围的很多朋友如今都有一款iWatch智能手表,通常都用来运动时监控心率,这也意味着iWatch不只能看时间这么简单。下面咱们定义一个iWatch类型:
type iWatch int // 定义一个iWatch类型,它实际上就是int型;至关于为int型取一个别名iWatch |
接下来为iWatch类型增长三个方法,分别为getHour()、getMinute()、getSecond()
// 为iWatch增长getHour()方法 func (w iWatch) getHour() int { return time.Now().Hour() } // 为iWatch增长getMinute()方法 func (w iWatch) getMinute() int { return time.Now().Minute() } // 为iWatch增长getSecond()方法 func (w iWatch) getSecond() int { return time.Now().Second() } |
下面是GO语言的精彩内容,请各位看客睁大眼睛:
func main() { var w watch // 定义类型为watch的变量w var t iWatch // 定义类型为iWatch的变量t w = t // 把类型为watch的变量w赋值给类型为iWatch的变量t,这样能行的通吗? fmt.Println("Current Hour:", w.getHour(), ", Minute:", w.getMinute(), ", Second:", w.getSecond()) } |
在这个测试代码中:
var w watch
至关于定义了一个接口变量
var t iWatch
至关于定义了一个iWatch对象
w = t
直接把对象t 赋给了接口变量w,但没有像其它面向对象语言那样,让iWatch implements watch,这样能行的通吗?
把“吗”字去掉,请看结果:
神奇吧 :)
以上是GO语言的接口实现。
(2)Java语言的接口实现
用面向对象的编程语言来解释:
在面向对象的编程语言中,好比Son是一个实现类,Father是一个接口,Son要实现Father接口,必须使用implements显式地声明,同时在Son中实现Father里面定义的方法,好比:
interface Father{
getHour();
}
class Son implements Father{
// 实现父接口Father定义的方法getHour()
public int getHour(){
return 20;
}
}
一旦接口Father增长一个方法getSecond(),那么实现该接口的全部孩儿都必须实现getSecond()方法。在使用时:
Father father = new Son();
即孩儿对象能够赋值给Father接口
【备注】:若对上面面向对象编程语言不熟悉的话,建议看一下设计模式相关的书籍
(3)侵入式接口和非侵入式接口
像上面(2)中的Java就是侵入式接口。
GO语言中,像上面(1)所示,尽管定义了接口watch,但实现类iWatch并无显示地声明实现该接口,只是watch中的方法都已在iWatch中实现,那么这种父子关系已创建,这种接口被称为“非侵入式接口”
七、引伸
上面例子中,为iWatch定义了三个方法,如今咱们修改一下这三个方法:
// 为*iWatch增长getHour()方法 func (w *iWatch) getHour() int { return time.Now().Hour() } // 为*iWatch增长getMinute()方法 func (w *iWatch) getMinute() int { return time.Now().Minute() } // 为*iWatch增长getSecond()方法 func (w *iWatch) getSecond() int { return time.Now().Second() } |
这至关于并非为iWatch类型增长了三个方法,而是为*iWatch类型增长了三个方法,那么调用时也须要相应修改:
func main() { var w watch var t iWatch w = &t fmt.Println("Current Hour:", w.getHour(), ", Minute:", w.getMinute(), ", Second:", w.getSecond()) } |
好了,关于GO语言的接口类型就聊到这里,请记住这么三句话:
interface是类型
interface类型的变量能够赋值
任何实现了interface类型的具体类型变量,均可以赋值给interface类型的变量