Go學習(11):介面
介面
1.1 什麼是介面?
面向物件世界中的介面的一般定義是“介面定義物件的行為”。它只指定物件應該做什麼。實現這種行為的方法(實現細節)是針對物件的。
在Go中,介面是一組方法簽名。當型別為介面中的所有方法提供定義時,它被稱為實現介面。它與OOP非常相似。介面指定了型別應該具有的方法,型別決定了如何實現這些方法。
它把所有的具有共性的方法定義在一起,任何其他型別只要實現了這些方法就是實現了這個介面
介面定義了一組方法,如果某個物件實現了某個介面的所有方法,則此物件就實現了該介面。
1.2 介面的定義語法
定義介面
/* 定義介面 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定義結構體 */
type struct_name struct {
/* variables */
}
/* 實現介面方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法實現 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法實現*/
}
示例程式碼:
package main
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
執行結果:
I am Nokia, I can call you!
I am iPhone, I can call you!
- interface可以被任意的物件實現
- 一個物件可以實現任意多個interface
- 任意的型別都實現了空interface(我們這樣定義:interface{}),也就是包含0個method的interface
1.3 interface值
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名欄位
school string
loan float32
}
type Employee struct {
Human //匿名欄位
company string
money float32
} //Human實現Sayhi方法
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
} //Human實現Sing方法
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
} //Employee重寫Human的SayHi方法
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
// Interface Men被Human,Student和Employee實現
// 因為這三個型別都實現了這兩個方法
type Men interface {
SayHi()
Sing(lyrics string)
}
func main() {
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
//定義Men型別的變數i
var i Men
//i能儲存Student
i = mike
fmt.Println("This is Mike, a Student:")
i.SayHi()
i.Sing("November rain")
//i也能儲存Employee
i = Tom
fmt.Println("This is Tom, an Employee:")
i.SayHi()
i.Sing("Born to be wild")
//定義了slice Men
fmt.Println("Let's use a slice of Men and see what happens")
x := make([]Men, 3)
//T這三個都是不同型別的元素,但是他們實現了interface同一個介面
x[0], x[1], x[2] = paul, sam, mike
for _, value := range x {
value.SayHi()
}
}
執行結果:
This is Mike, a Student:
Hi, I am Mike you can call me on 222-222-XXX
La la la la... November rain
This is Tom, an Employee:
Hi, I am Sam, I work at Things Ltd.. Call me on 444-222-XXX
La la la la... Born to be wild
Let's use a slice of Men and see what happens
Hi, I am Paul you can call me on 111-222-XXX
Hi, I am Sam, I work at Golang Inc.. Call me on 444-222-XXX
Hi, I am Mike you can call me on 222-222-XXX
那麼interface裡面到底能存什麼值呢?如果我們定義了一個interface的變數,那麼這個變數裡面可以存實現這個interface的任意型別的物件。例如上面例子中,我們定義了一個Men interface型別的變數m,那麼m裡面可以存Human、Student或者Employee值
當然,使用指標的方式,也是可以的
但是,介面物件不能呼叫實現物件的屬性
interface函式引數
interface的變數可以持有任意實現該interface型別的物件,這給我們編寫函式(包括method)提供了一些額外的思考,我們是不是可以通過定義interface引數,讓函式接受各種型別的引數
嵌入interface
package main
import "fmt"
type Human interface {
Len()
}
type Student interface {
Human
}
type Test struct {
}
func (h *Test) Len() {
fmt.Println("成功")
}
func main() {
var s Student
s = new(Test)
s.Len()
}
執行結果:
成功
示例程式碼:
package test
import (
"fmt"
)
type Controller struct {
M int32
}
type Something interface {
Get()
Post()
}
func (c *Controller) Get() {
fmt.Print("GET")
}
func (c *Controller) Post() {
fmt.Print("POST")
}
package main
import (
"fmt"
"test"
)
type T struct {
test.Controller
}
func (t *T) Get() {
//new(test.Controller).Get()
fmt.Print("T")
}
func (t *T) Post() {
fmt.Print("T")
}
func main() {
var something test.Something
something = new(T)
var t T
t.M = 1
// t.Controller.M = 1
something.Get()
}
示例程式碼:
T
Controller實現了所有的Something介面方法,當結構體T中呼叫Controller結構體的時候,T就相當於Java中的繼承,T繼承了Controller,因此,T可以不用重寫所有的Something介面中的方法,因為父構造器已經實現了介面。
如果Controller沒有實現Something介面方法,則T要呼叫Something中方法,就要實現其所有方法。
如果something = new(test.Controller)
則呼叫的是Controller中的Get方法。
T可以使用Controller結構體中定義的變數
總結
介面物件不能呼叫介面實現物件的屬性