GoLang之方法与接口
GoLang之方法與接口
?
Go語言沒有沿襲傳統面向對象編程中的諸多概念,比如繼承、虛函數、構造函數和析構函數、隱藏的this指針等。
?
方法
Go 語言中同時有函數和方法。方法就是一個包含了接受者(receiver)的函數,receiver可以是內置類型或者結構體類型的一個值或者是一個指針。所有給定類型的方法屬于該類型的方法集。
如下面的這個例子,定義了一個新類型Integer,它和int一樣,只是為它內置的int類型增加了個新方法Less()
type Integer int func (a Integer) Less(b Integer) bool {return a < b }func main() {var a Integer = 1 if a.Less(2) {fmt.Println("less then 2")} }可以看出,Go語言在自定義類型的對象中沒有C++/Java那種隱藏的this指針,而是在定義成員方法時顯式聲明了其所屬的對象。
?
method的語法如下:
func (r ReceiverType) funcName(parameters) (results)當調用method時,會將receiver作為函數的第一個參數:
funcName(r, parameters);所以,receiver是值類型還是指針類型要看method的作用。如果要修改對象的值,就需要傳遞對象的指針。
指針作為Receiver會對實例對象的內容發生操作,而普通類型作為Receiver僅僅是以副本作為操作對象,并不對原實例對象發生操作。
func (a *Ingeger) Add(b Integer) {*a += b }func main() {var a Integer = 1 a.Add(3)fmt.Println("a =", a) // a = 4 }如果Add方法不使用指針,則a返回的結果不變,這是因為Go語言函數的參數也是基于值傳遞。
注意:當方法的接受者是指針時,即使用值類型調用那么方法內部也是對指針的操作。
?
之前說過,Go語言沒有構造函數的概念,通常使用一個全局函數來完成。例如:
func NewRect(x, y, width, height float64) *Rect {return &Rect{x, y, width, height} } func main() {rect1 := NewRect(1,2,10,20)fmt.Println(rect1.width) }?
?
匿名組合
Go語言提供了繼承,但是采用了組合的語法,我們將其稱為匿名組合,例如:
type Base struct {name string }func (base *Base) Set(myname string) {base.name = myname }func (base *Base) Get() string {return base.name }type Derived struct {Baseage int }func (derived *Derived) Get() (nm string, ag int) {return derived.name, derived.age }func main() {b := &Derived{}b.Set("sina")fmt.Println(b.Get()) }例子中,在Base類型定義了get()和set()兩個方法,而Derived類型繼承了Base類,并改寫了Get()方法,在Derived對象調用Set()方法,會加載基類對應的方法;而調用Get()方法時,加載派生類改寫的方法。
?
組合的類型和被組合的類型包含同名成員時,?會不會有問題呢?可以參考下面的例子:
type Base struct {name stringage int }func (base *Base) Set(myname string, myage int) {base.name = mynamebase.age = myage }type Derived struct {Basename string }func main() {b := &Derived{}b.Set("sina", 30)fmt.Println("b.name =",b.name, "\tb.Base.name =", b.Base.name)fmt.Println("b.age =",b.age, "\tb.Base.age =", b.Base.age) }?
?
?
值語義和引用語義
值語義和引用語義的差別在于賦值,比如
b = a b.Modify()如果b的修改不會影響a的值,那么此類型屬于值類型;如果會影響a的值,那么此類型是引用類型。
Go語言中的大多數類型都基于值語義,包括:
- 基本類型,如byte、int、bool、float32、string等;
- 復合類型,如arry、struct、pointer等;
?
C語言中的數組比較特別,通過函數傳遞一個數組的時候基于引用語義,但是在結構體定義數組變量的時候基于值語義。而在Go語言中,數組和基本類型沒有區別,是很純粹的值類型,例如:
var a = [3] int{1,2,3} var b = a b[1]++ fmt.Println(a, b) // [1 2 3] [1 3 3]從結果看,b=a賦值語句是數組內容的完整復制,要想表達引用,需要用指針:
var a = [3] int{1,2,3} var b = &a // 引用語義 b[1]++ fmt.Println(a, b) // [1 3 3] [1 3 3]?
?
接口
Interface 是一組抽象方法(未具體實現的方法/僅包含方法名參數返回值的方法)的集合,如果實現了 interface 中的所有方法,即該類/對象就實現了該接口。
Interface 的聲明格式:
type interfaceName interface { //方法列表 }Interface 可以被任意對象實現,一個類型/對象也可以實現多個 interface;
interface的變量可以持有任意實現該interface類型的對象。
?如下面的例子:
package mainimport "fmt"type Human struct {name stringage intphone string}type Student struct {Human //匿名字段school stringloan float32}type Employee struct {Human //匿名字段company stringmoney 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)}// 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{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}//定義Men類型的變量ivar i Men//i能存儲Studenti = mike fmt.Println("This is Mike, a Student:")i.SayHi()i.Sing("November rain")//i也能存儲Employeei = tomfmt.Println("This is tom, an Employee:")i.SayHi()i.Sing("Born to be wild")//定義了slice Menfmt.Println("Let's use a slice of Men and see what happens")x := make([]Men, 3)//這三個都是不同類型的元素,但是他們實現了interface同一個接口x[0], x[1], x[2] = paul, sam, mikefor _, value := range x{value.SayHi()}}?
空接口
空interface(interface{})不包含任何的method,正因為如此,所有的類型都實現了空interface。空interface對于描述起不到任何的作用(因為它不包含任何的method),但是空interface在我們需要存儲任意類型的數值的時候相當有用,因為它可以存儲任意類型的數值。它有點類似于C語言的void*類型。
// 定義a為空接口var a interface{}var i int = 5s := "Hello world"// a可以存儲任意類型的數值a = ia = s?
interface的變量里面可以存儲任意類型的數值(該類型實現了interface),那么我們怎么反向知道這個interface變量里面實際保存了的是哪個類型的對象呢?目前常用的有兩種方法:switch測試、Comma-ok斷言。
?
switch測試如下:
type Element interface{} type List [] Elementtype Person struct {name stringage int }//打印 func (p Person) String() string {return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" }func main() {list := make(List, 3)list[0] = 1 //an int list[1] = "Hello" //a stringlist[2] = Person{"Dennis", 70} for index, element := range list{switch value := element.(type) {case int:fmt.Printf("list[%d] is an int and its value is %d\n", index, value)case string:fmt.Printf("list[%d] is a string and its value is %s\n", index, value)case Person:fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)default:fmt.Println("list[%d] is of a different type", index)} } }?
如果使用Comma-ok斷言的話:
func main() {list := make(List, 3)list[0] = 1 // an intlist[1] = "Hello" // a stringlist[2] = Person{"Dennis", 70}for index, element := range list {if value, ok := element.(int); ok {fmt.Printf("list[%d] is an int and its value is %d\n", index, value)} else if value, ok := element.(string); ok {fmt.Printf("list[%d] is a string and its value is %s\n", index, value)} else if value, ok := element.(Person); ok {fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)} else {fmt.Printf("list[%d] is of a different type\n", index)}} }?
?
嵌入接口
正如struct類型可以包含一個匿名字段,interface也可以嵌套另外一個接口。
如果一個interface1作為interface2的一個嵌入字段,那么interface2隱式的包含了interface1里面的method。
?
?
?
參考文檔:
http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/
http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/
?
總結
以上是生活随笔為你收集整理的GoLang之方法与接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 七个非常好用的黑科技APP,免费/小众/
- 下一篇: 制作完整的java可执行文件