Go 语言不是一种 “传统” 的面向对象编程语言:它里面没有类和继承的概念。
但是 Go 语言里有非常灵活的 接口 概念,通过它可以实现很多面向对象的特性。接口提供了一种方式来 说明 对象的行为:如果谁能搞定这件事,它就可以用在这儿。
简单的说,interface是一组method的组合,我们通过interface来定义对象的一组行为,但是这些method接口不需要去实现,并且interface不能包含任何变量。到某个自定义类型(比如结构体Human)要使用的时候,再根据具体情况把这些方法写出来(实现这个方法)。
接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。
通过如下格式定义接口:
type 接口名 interface { Method1(param_list) return_type Method2(param_list) return_type ... }示例:
//定义一个人类接口 type Human interface { Run() //奔跑 Laugh() //笑 Speak(words string) //说话 } interface类型interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口。
示例:
package main import ( "fmt" ) //定义三个接口 type Men interface { SayHi() Sing(lyrics string) Guzzle(beerStein string) } type YoungChap interface { SayHi() Sing(song string) BorrowMoney(amount float32) } type ElderlyGent interface { SayHi() Sing(song string) SpendSalary(amount float32) } //人类 type Human struct { name string age int phone string } func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } func (h *Human) Sing(lyrics string) { fmt.Println("La la, la la la, la la la la...", lyrics) } func (h *Human) Guzzle(beerStein string) { fmt.Println("Guzzle Guzzle Guzzle...", beerStein) } //学生 type Student struct { Human school string loan float32 } func (s *Student) BorrowMoney(amount float32) { s.loan += amount } //雇员 type Employee struct { Human company string money float32 } func (e *Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s, Call me on %s", e.name, e.company, e.phone) } func (e *Employee) SpendSalary(amount float32) { e.money -= amount } func main() { var tmp Men = &Student{Human{"itbsl", 23, "176xxxx3456"}, "北京邮电大学", 34} fmt.Println(tmp.(*Student).name) }输出结果:
Hi, I am itbsl you can call me on 176xxxx3456通过上面的代码我们可以看到,interface可以被任意的对象实现。我们看到上面的Men interface被Human、Student和Employee实现。同理,一个对象可以实现任意多个interface,例如上面的Student实现了Men和YoungChap两个interface。
注: 上面实现接口类型的都是各个结构体的指针类型,所以接口变量存储的也只能是各个结构体类型的指针类型。如果实现接口的是各个结构体的值类型,那么接口变量能存储的就是指类型。
只要某个对象实现某个接口,那么该接口类型的变量可以用来存储该对象。上面我们把Student实例赋值给了一个Men类型的接口变量。
不仅仅是结构体类型,只要是自定义数据类型,就可以实现接口
typ Test interface { Say() } type integer int func(i integer) Say() { fmt.Println("integer Say i = ", i) } var i integer = 10 var b Test = i i.Say() 空接口(interface{})空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。
示例:
// 定义a为空接口 var a interface{} var i int = 5 s := "Hello world" // a可以存储任意类型的数值 a = i a = s一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!
interface函数参数interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数。
举个例子:fmt.Println是我们常用的一个函数,但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件,你会看到这样一个定义:
type Stringer interface { String() string }也就是说,任何实现了String方法的类型都能作为参数被fmt.Println调用,让我们来试一试
package main import ( "fmt" "strconv" ) type Human struct { name string age int phone string } // 通过这个方法 Human 实现了 fmt.Stringer func (h Human) String() string { return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years - ✆ " +h.phone+"❱" } func main() { Bob := Human{"Bob", 39, "000-7777-XXX"} fmt.Println("This Human is : ", Bob) }