通常我們在做項目的時候,要手動搭建項目的結構,如controller,service,mapper,entity,是不是很麻煩,特別是資料庫表特別多時,現在介紹一下使用MybatisPlus時怎麼自動生成這些代碼。 1. 首先要在項目的pom.xml里引入必要的依賴,如下: ~~~xml com.b ...
概述
方法是面向對象編程 (OOP) 的一個特性,在 C++/Java 語言中方法是類函數,go做為函數式編程語言,通過特有技能支持相似的功能,所以說go也支持面向對象編程特性。
go 方法本質也是函數,相比普通函數稍有區別,方法必須與具體類型綁定,且無法獨立運行,只能通過類型實例執行,函數是一等公民,方法是二等公民。方法很像面向對象的類方法,但又有區別,方法更加鬆散,耦合性更低。類方法是定義在對象內部,go方法更像是一種屬性擴展,在不修改類型定義的情況下可擴展方法,比如可為外部引入的結構體增加方法,這在Java
、C#等面向對象語言是不允許的。
基本使用
方法可以綁定到任意類型,但是實際情況總是與結構體綁定,兩者結合可以模擬面向對象特性,當然僅是模擬,文章主要使用結構體演示。
定義類型
type Person struct { Age int Name string }
為結構體定義方法
func (p *Person) sayHi() { // 為結構體定義了一個方法 fmt.Println("hi, I'm ", p.Name) }
相比普通函數定義稍有區別,在func
關鍵字和函數名之間增加一個接收器,或稱為接收者、連接器等,如上接收器是Person
類型指針。與接收器參數匹配的類型,就增加了一個新方法,目前觀察這是go的獨創。
方法方必須依實例,無法獨立執行。與面型對象中方法一樣,先有對象,才能調用方法。
sayHi() // error undefined: sayHi
必須通過結構體實例調用,會自動把實例傳遞給方法的接收器,類似Java
的this
、Python
的self
,隱式傳遞第一個參數。
p1 := Person{Name: "tom"} p1.sayHi() // hi, I'm tom
為簡單理解,編譯後偽代碼如下
func sayHi(self) { // self是體結構體指針 fmt.Println("hi, I'm ", self.Name) }
接收器參數類型,除非有明確需求,否則都應該使用指針。同樣的問題,使用值類型本質是每次調用,都傳入複製的新實例。
func (p Person) sayHi() { p.name = "tony" fmt.Println("hi, I'm ", p.Name) // tony } func main() { p1 := Person{Name: "tom"} p1.sayHi() // 複製新結構體傳遞給sayHi fmt.Println(p1.Name) // tom }
語法層面沒有限制,允許為任何類型創建方法,包括基礎數據類型。但是有一個限制,方法和類型定義必須在同一個包,為基礎數據類型、或引入第三方類型定義方法需要一些變通,先使用type
定義類型,在擴展方法。
為基本數據類型擴展方法
type Integer int func (m *Integer) Value() { fmt.Println(*m) }
使用方法
var m Integer = 10 m.Value() // 10
假如Person是從第三方引入的類型,為其擴展新方法
type MyPerson Person func (m *MyPerson) getName() { return Person(*m).Name // 先強制轉為Person類型,再讀取Name屬性 }
使用方法
p1 := MyPerson{Name: "tom"} p1.getName() p1.sayHi // err
sayHi
是Person結構體的方法,無法通過MyPerson類型訪問,也不支持常規的繼承
更優雅是使用組合的方式(繼承模式),Go
語言不支持傳統面向對象中的繼承特性,而是以自己特有的組合方式支持了方法的繼承,通過在結構體內置匿名的成員來實現繼承
type MyPerson struct { City string Person // 匿名屬性 } func (m *MyPerson) getCity() { return m.City }
使用方法
p1 := MyPerson{City: "shanghai"} p1.getCity() // shanghai p1.sayHi() // ok,調用繼承方法
被繼承的對象稱為基礎類型,屬性、方法都可以被繼承,註意繼承方法在調用時連接器接收的對象是原始對象,如上案例中sayHi
接收的是Person
對象,而非MyPerson
對象,這種展開是編譯期完成的, 並沒有運行時代價。這與C++、C#、Java主流面向對象語言不同,子類方法在運行時動態綁定到對象,因此基類某些方法看到的 this 可能不是基類類型對應的對象,這個特性會導致基類方法運行的不確定性。
方法是由函數演變而來,只是將函數第一個參數移動到函數名前面而已,方法是特殊的函數。兩者特性幾乎一樣,比如都是值傳遞、都不支持重載,甚至通過方法表達式可以將方法還原為普通函數
sayHi := (*Person).sayHi // 方法轉換為函數 p := &Person{Age: 1, Name: "xk"} // 創建結構體 sayHi(p) // 調用轉換後的函數
轉換為普通函數後,將接收器轉換為函數的第一個參數,調用時候需要顯示傳遞參數。