您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Go語言多態和interface如何使用”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Go語言多態和interface如何使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
對于Java
或者是C++
而言,我們在使用變量的時候,變量的類型是明確的。但是如果我們希望它可以寬松一點,比如說我們用父類指針或引用去調用方法,但是在執行的時候,能夠根據子類的類型去執行子類當中的方法。也就是說實現我們用相同的調用方式調出不同結果或者是功能的情況,這種情況就叫做多態。
(推薦課程:Go教程)
舉個非常經典的例子,比如說貓、狗和人都是哺乳動物。這三個類都有一個say
方法,大家都知道貓、狗以及人類的say
是不一樣的,貓可能是喵喵叫,狗是汪汪叫,人類則是說話。
class Mammal { public void say() { System.out.println("do nothing") } } class Cat extends Mammal{ public void say() { System.out.println("meow"); } } class Dog extends Mammal{ public void say() { System.out.println("woof"); } } class Human extends Mammal{ public void say() { System.out.println("speak"); } }
這段代碼大家應該都不難看懂,這三個類都是Mammal
的子類,假設這個時候我們有一系列實例,它們都是Mammal
的子類的實例,但是這三種類型都有,我們希望用一個循環來一起全都調用了。雖然我們接收變量的時候是用的Mammal
的父類類型去接收的,但是我們調用的時候卻會獲得各個子類的運行結果。
比如這樣:
class Main { public static void main(String[] args) { List<Mammal> mammals = new ArrayList<>(); mammals.add(new Human()); mammals.add(new Dog()); mammals.add(new Cat()); for (Mammal mammal : mammals) { mammal.say(); } } }
不知道大家有沒有get
到精髓,我們創建了一個父類的List
,將它各個子類的實例放入了其中。然后通過了一個循環用父類對象來接收,并且調用了say
方法。我們希望雖然我們用的是父類的引用來調用的方法,但是它可以自動根據子類的類型調用對應不同子類當中的方法。
也就是說我們得到的結果應該是:
speak woof meow
這種功能就是多態,說白了我們可以在父類當中定義方法,在子類當中創建不同的實現。但是在調用的時候依然還是用父類的引用去調用,編譯器會自動替我們做好內部的映射和轉化。
這樣實現當然是可行的,但其實有一個小小的問題,就是Mammal
類當中的say
方法多余了。因為我們使用的只會是它的子類,并不會用到Mammal
這個父類。所以我們沒必要實現父類Mammal
中的say
方法,做一個標記,表示有這么一個方法,子類實現的時候需要實現它就可以了。
這就是抽象類和抽象方法的來源,我們可以把Mammal
做成一個抽象類,聲明say
是一個抽象方法。抽象類是不能直接創建實例的,只能創建子類的實例,并且抽象方法也不用實現,只需要標記好參數和返回就行了。具體的實現都在子類當中進行。說白了抽象方法就是一個標記,告訴編譯器凡是繼承了這個類的子類必須要實現抽象方法,父類當中的方法不能調用。那抽象類就是含有抽象方法的類。
我們寫出Mammal
變成抽象類之后的代碼:
abstract class Mammal { abstract void say(); }
很簡單,因為我們只需要定義方法的參數就可以了,不需要實現方法的功能,方法的功能在子類當中實現。由于我們標記了say
這個方法是一個抽象方法,凡是繼承了Mammal
的子類都必須要實現這個方法,否則一定會報錯。
(推薦課程:Go Web編程)
抽象類其實是一個擦邊球,我們可以在抽象類中定義抽象的方法也就是只聲明不實現,也可以在抽象類中實現具體的方法。在抽象類當中非抽象的方法子類的實例是可以直接調用的,和子類調用父類的普通方法一樣。但假如我們不需要父類實現方法,我們提出提取出來的父類中的所有方法都是抽象的呢?針對這一種情況,Java
當中還有一個概念叫做接口,也就是interface
,本質上來說interface
就是抽象類,只不過是只有抽象方法的抽象類。
所以剛才的Mammal
也可以寫成:
interface Mammal { void say(); }
把Mammal
變成了interface
之后,子類的實現沒什么太大的差別,只不過將extends
關鍵字換成了implements
。另外,子類只能繼承一個抽象類,但是可以實現多個接口。早先的Java
版本當中,interface
只能夠定義方法和常量,在Java8
以后的版本當中,我們也可以在接口當中實現一些默認方法和靜態方法。
接口的好處是很明顯的,我們可以用接口的實例來調用所有實現了這個接口的類。也就是說接口和它的實現是一種要寬泛許多的繼承關系,大大增加了靈活性。
以上雖然全是Java
的內容,但是講的其實是面向對象的內容,如果沒有學過Java
的小伙伴可能看起來稍稍有一點點吃力,但總體來說問題不大,沒必要細扣當中的語法細節,get
到核心精髓就可以了。
講這么一大段的目的是為了厘清面向對象當中的一些概念,以及接口的使用方法和理念,后面才是本文的重頭戲,也就是Go
語言當中接口的使用以及理念。
Golang
當中也有接口,但是它的理念和使用方法和Java
稍稍有所不同,它們的使用場景以及實現的目的是類似的,本質上都是為了抽象。通過接口提取出了一些方法,所有繼承了這個接口的類都必然帶有這些方法,那么我們通過接口獲取這些類的實例就可以使用了,大大增加了靈活性。
但是Java
當中的接口有一個很大的問題就是侵入性,說白了就是會顛倒供需關系。舉個簡單的例子,假設你寫了一個爬蟲從各個網頁上爬取內容。爬蟲爬到的內容的類別是很多的,有圖片、有文本還有視頻。假設你想要抽象出一個接口來,在這個接口當中定義你規定的一些提取數據的方法。這樣不論獲取到的數據的格式是什么,你都可以用這個接口來調用。這本身也是接口的使用場景,但問題是處理圖片、文本以及視頻的組件可能是開源或者是第三方的,并不是你開發的。你定義接口并沒有什么卵用,別人的代碼可不會繼承這個接口。
當然這也是可以解決的, 比如你可以在這些第三方工具庫外面自己封裝一層,實現你定義的接口。這樣當然是OK的,但是顯然比較麻煩。
(推薦微課:Go微課)
Golang
當中的接口解決了這個問題,也就是說它完全拿掉了原本弱化的繼承關系,只要接口中定義的方法能對應的上,那么就可以認為這個類實現了這個接口。
我們先來創建一個interface
,當然也是通過type
關鍵字:
type Mammal interface { Say() }
我們定義了一個Mammal
的接口,當中聲明了一個Say
函數。也就是說只要是擁有這個函數的結構體就可以用這個接口來接收,我們和剛才一樣,定義Cat
、Dog
和Human
三個結構體,分別實現各自的Say
方法:
type Dog struct{} type Cat struct{} type Human struct{} func (d Dog) Say() { fmt.Println("woof") } func (c Cat) Say() { fmt.Println("meow") } func (h Human) Say() { fmt.Println("speak") }
之后,我們嘗試使用這個接口來接收各種結構體的對象,然后調用它們的Say
方法:
func main() { var m Mammal m = Dog{} m.Say() m = Cat{} m.Say() m = Human{} m.Say() }
讀到這里,這篇“Go語言多態和interface如何使用”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。