Go語言的接口interface、struct和組合、繼承


Go語言的interface概念相對於C++中的基類,通過interface來實現多態功能。

在C++中,當需要實現多態功能時,步驟是首先定義一個基類,該基類使用虛函數或者純虛函數抽象了所有子類會用到的共同的最基本的成員函數,之后子類繼承該基類,然后每個派生類自定義自己的虛函數實現。最后在使用基類指針或者引用的地方傳入派生類,程序根據具體傳入的對象來調用對象的函數。

在Go中,定義一個interface類型,該類型說明了它有哪些方法,這就完成了類似C++中的基類定義,然后在其他的函數中,將該interface類型作為函數的形參,任意一個實現了interface類型的實參都能作為該interface的實例對象。interface類型和作為interface類型的實參對象之間就相當於存在繼承關系,或者說叫實現接口(Java說法),但這種繼承關系(實現接口)是隱式的(自動的),也即我們無需主動說明(顯式implements實現)該實參類型是某interface類型的派生類,代碼如下:

type base interface { //類似基類定義
	virtualfunc() int //類似虛函數抽象定義
}

type der1 int //類似派生類1定義

func (der1) virtualfunc() int { //類似派生類1虛函數具體實現
	fmt.Printf("I'm der1\n")
	return 1
}

type der2 struct { //類似派生類2定義
	//nothing
}

func (der2) virtualfunc() int { //類似派生類2虛函數具體實現
	fmt.Printf("I'm der2\n")
	return 2
}

func somefunc(b base) { //作為某個函數的形參
	b.virtualfunc()
}

上述代碼中base是interface類型,b作為somefunc( )函數的形參,因為base接口類型要求實現virtualfunc( )函數,而der1和der2都實現了該函數,因為der1和der2自動是base的派生類,在somefunc( )函數中,要求base類型的實參時,可以用der1或者der2的實例對象傳入,這就實現了不同類型不同行為,也即多態。這是Go實現面向對象特性的一種特殊方法,並不是通過類和繼承來完成的,Go也沒有繼承這種功能。

上面的代碼並沒有說明interface的全部特性,還有一些說明如下:

  1. 實現某個接口的類型(如上面的der2)可以有其他的方法。這正如派生類還可以額外增加基類並沒有的成員函數一樣,但增加的函數不是接口中的部分。
  2. 一個類型可以實現多個接口。這類似於C++的多繼承,一個派生類可以當做多種基類來使用。

從somefunc( )函數中的形實參結合可以看到,我們能定義一個interface類型的變量,並用一個“派生類”去初始化或者賦值,代碼如下:

func main() {
	var baseobj base

	var d1 der1
	baseobj = d1
	somefunc(baseobj)

	var d2 der2
	baseobj = d2
	somefunc(baseobj)
}

上面代碼中,第2行是定義了一個默認初始化的interface base類型對象,第5行和第9行代碼則是用兩個“派生類”去賦值interface base類型對象。運行結果如下:

I'm der1
I'm der2

 

Go中沒有繼承的功能,它是通過接口來實現類似功能,Go中還有一種叫做組合的概念,如下:

package main

import (
	"fmt"
)

type Base struct {
	// nothing
}

func (b *Base) ShowA() {
	fmt.Println("showA")
	b.ShowB()
}
func (b *Base) ShowB() {
	fmt.Println("showB")
}

type Derived struct {
	Base
}

func (d *Derived) ShowB() {
	fmt.Println("Derived showB")
}

func main() {
	d := Derived{}
	d.ShowA()
}

上述代碼執行結果不會輸出“Derived showB”,因為Go中沒有繼承的概念,只有組合,上面的Derived包含了Base,自動的含有了Base的方法,因為其不是繼承,所以不會根據具體傳入的對象而執行對應的方法。

 

下面的的代碼又說明了type name和type struct的區別:

package main

import (
	"fmt"
)

type Mutex struct {
	// nothing
}

func (m *Mutex) Lock() {
	fmt.Println("mutex lock")
}

func (m *Mutex) Unlock() {
	fmt.Println("mutex unlock")
}

type newMutex Mutex

type structMutex struct {
	Mutex
}

func main() {
	m1 := Mutex{}
	m1.Lock()

	// n1 := newMutex{}   
	// n1.Lock()      沒有Lock()方法

	x1 := structMutex{}
	x1.Lock()

}

上面的代碼中n1不能執行Lock( )函數,因為Golang不支持隱式類型轉換,雖然newMutex就是Mutex,但語法上它們是兩種類型,因此newMutex不能執行Lock( )方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM