golang中並沒有明確的面向對象的說法,實在要扯上的話,可以將struct比作其它語言中的class。
類聲明
type Book struct {
Title string
Author string
intro string
}
這樣就聲明了一個類,其中沒有public、protected、private的的聲明。golang用另外一種做法來實現屬性的訪問權限:屬性的開頭字母是大寫的則在其它包中可以被訪問,否則只能在本包中訪問。類的聲明和方法亦是如此。
類方法聲明
// 類方法聲明-傳遞值對象
func (b Book) B1() {
b.Title = "Book001"
b.Author = "ErWan"
}
// 類方法聲明-傳遞指針對象
func (b *Book) B2() {
b.Title = "Book002"
b.Author = "Tinywan"
}
和其它語言不一樣,golang聲明方法和普通方法一致,只是在func后增加了b Book這樣的聲明。加和沒有加*的區別在於一個是傳遞指針對象(加*),一個是傳遞值對象。
傳遞指針和對象的區別
package main
import "fmt"
// 類聲明
type Book struct {
Title string
Author string
DateTime string
}
// 類方法聲明-傳遞值對象
func (b Book) B1() {
b.Title = "Book001"
b.Author = "ErWan"
}
// 類方法聲明-傳遞指針對象
func (b *Book) B2() {
b.Title = "Book002"
b.Author = "Tinywan"
}
func main() {
/*聲明一個 Book 類型的變量 b ,並調用 B1() 和 B2()*/
b := Book{"Def-Book", "Def-Author", "Def-DateTime"}
fmt.Println("B1 調用前:", b.Title, b.Author, b.DateTime)
b.B1()
fmt.Println("B1 調用后:", b.Title)
fmt.Println("------------------\r\n")
fmt.Println("B2 調用前:", b.Title)
b.B2()
fmt.Println("B2 調用后:", b.Title)
}
執行結果:
B1() 的接收者是值類型 Book, B2() 的接收者是值類型 *Book , 兩個方法內都是改變Name值。
小結:
1、接收者可以看作是函數的第一個參數,即這樣的: func B1(b Book), func B2(b *Book)。 go不是面向對象的語言,所以用那種看起來像面向對象的語法來理解可能有偏差。
2、當調用 b.B1() 時相當於 B1(b) ,實參和行參都是類型 Book,可以接受。此時在B1()中的b只是 "Def-Book" 的值拷貝,所以B1()的修改影響不到" Def-Book"。
3、當調用 b.B2() => B2(b1),這是將 Book 類型傳給了 *Book 類型,go可能會取 b 的地址傳進去: B2(&b)。所以 B2() 的修改可以影響 b 。
例如在一個beego 框架中我們要修改一個action的值,是這么定義的(為了修改內部結構的值。而不是傳遞一下)
func (this *InputController) InputGet(){
// Get 方式接受 name := this.GetString("name") // 不使用模版,直接用 this.Ctx.WriteString 輸出字符串 this.Ctx.WriteString(name) }
值類型不可以改變值,而指針類型則是可以的
匿名結構體
p := struct {
Name string
Gender string
Age uint8
}{"Robert", "Male", 33}
匿名結構體最大的用處是在內部臨時創建一個結構以封裝數據,而不必正式為其聲明相關規則。
實例化對象
實例化對象有好幾種方式:
package main
// 類聲明
type Person struct {
Name string
Age int
Doc []string // slice切片
}
// 類方法聲明-傳遞值對象
func (p *Person) P1() {
p.Name = "Tinywan"
p.Age = 24
}
// 類方法聲明-傳遞指針對象
func (p *Person) P2() {
p.Name = "Tinyaiai"
p.Age = 22
}
func main() {
// 實例化對象 實例化的時候可以初始化屬性值,如果沒有指明則默認為系統默認值
p1 := &Person{}
p1.Name = "ShaoBo Wan"
p1.Age = 20
p2 := &Person{Name:"HaHa"}
p3 := new(Person)
p3.Name = "New Name"
p4 := Person{}
p4.Name = "Name 001"
p4.Age = 26
p5 := Person{Name:"Name 002",Age:28}
// 使用中如果包含數組(動態數組 slice切片),結構體的實例化需要加上類型如上如果intro的類型是[]string
p6 := &Person{
"zhangsan",
25,
[]string{"lisi", "wangwu"},
}
}
注意,最后一個實例化
p6 := &Person{
"zhangsan",
25,
[]string{"lisi", "wangwu"},
}
小結:
1、使用中如果包含數組,結構體的實例化需要加上類型如上如果Doc的類型是[]string。
2、實例化的時候可以初始化屬性值,如果沒有指明則默認為系統默認值。
3、加&符號和new的是指針對象,沒有的則是值對象,這點和php、java不一致,在傳遞對象的時候要根據實際情況來決定是要傳遞指針還是值。
4、當對象比較小的時候傳遞指針並不划算。
繼承
確切的說golang中叫做組合(composition)
1、先初始化為空再賦值
// 先初始化為空再賦值
s1 := &Student{}
s1.schoole = "QiHua"
2、直接賦值
// 直接賦值
s2 := &Student{
Persons: Persons{
Name:"Tinywan",
Age:24,
Doc:[]string{"H1","h2"},
},
schoole:"BeiJin Schoole",
}
3、完整代碼
package main
import (
"fmt"
)
// 類聲明
type Persons struct {
Name string
Age int
Doc []string // slice切片
}
// 獲取Name
func (p *Persons) getName() {
fmt.Println("Name is ",p.Name)
}
type Student struct {
// Student 屬性中聲明了 Persons,表示組合了Persons 的屬性和方法(屬性和方法都會被繼承)
Persons
Name string
schoole string
}
func main() {
// 先初始化為空再賦值
s1 := &Student{}
// 當訪問Name的時候默認為ProsePoem的 Name,如果需要訪問Persons 的 Name 屬性可以使用 s1.Persons.Name 來訪問方法同理。
s1.Name = "Tinywan"
s1.Persons.Name = "Persons Name"
s1.schoole = "QiHua"
fmt.Println("s1 = ",s1)
fmt.Println("\r\n")
// 直接賦值
s2 := &Student{
Persons: Persons{
Name:"Tinywan",
Age:24,
Doc:[]string{"s2-Doc","s2-Doc"},
},
Name:"ErWan",
schoole:"BeiJin-Schoole",
}
fmt.Println("s2 = ",s2)
}
方法的繼承和屬性一致
參考
1、https://segmentfault.com/a/1190000012325027
2、