go語言基礎語法:面向物件程式設計
阿新 • • 發佈:2018-12-16
一、匿名組合
1.匿名欄位初始化
type Person struct { name string sex byte age int } type Student struct { Person//只有型別,沒有名字,匿名欄位,繼承了Person裡面的成員 id int addr string } func main() { //順序初始化 var s1 Student = Student{Person{"mike", 'm', 18}, 1, "bj"} fmt.Println("s1=", s1) //自動推導型別 s2 := Student{Person{"mike", 'm', 18}, 1, "bj"} fmt.Println("s2=", s2) //%+v,顯示更詳細 fmt.Printf("s2=%+v\n", s2) //指定成員初始化,沒有初始化的成員自動賦值為0 s3 := Student{Person: Person{name: "mike"}, id: 1} fmt.Printf("s3=%+v\n", s3) }
2.成員的操作
Person和Student結構體成員的定義如上
var s1 Student = Student{Person{"mike", 'm', 18}, 1, "bj"}
s1.name = "yoyo"
s1.sex = 'f'
s1.age = 22
s1.id = 2
s1.addr = "sz"
fmt.Printf("s1=%+v\n", s1)
s1.Person = Person{"go", 'm', 18}
fmt.Println(s1.name, s1.sex, s1.age, s1.id, s1.addr)
3.同名欄位
Person結構體的定義如上
type Student struct { Person id int addr string name string//與Person中的欄位同名了 } func main() { //宣告一個變數 var s Student //預設規則(就近原則),如果能在本作用域找到此成員,就操作此成員 //如果沒有找到,就找繼承的欄位 s.name = "mike" s.sex = 'm' s.age = 18 s.addr = "bj" fmt.Printf("s=%+v\n", s) //顯式呼叫 s.Person.name = "yoyo"//指明給Person的name賦值 fmt.Printf("s=%+v\n", s) }
4.非結構體匿名欄位
Person結構體定義如上
type mystr string//自定義型別,給一個型別改名
type Student struct {
Person//結構體匿名欄位
int //基礎型別的匿名欄位(非結構體匿名欄位)
mystr
}
func main() {
s := Student{Person{"mike", 'm', 18}, 1, "bj"}
fmt.Printf("s=%+v\n", s)
s.Person = Person{"go", 'm', 22}
fmt.Println(s.name, s.age, s.sex, s.int, s.mystr)
fmt.Println(s.Person, s.int, s.mystr)
}
5.結構體指標型別匿名欄位
type Student struct {
*Person//指標型別
id int
addr string
}
func main() {
s1 := Student{&Person{"mike", 'm', 18}, 1, "bj"}
fmt.Printf("s1=%+v\n", s1)//Person部分顯式的是地址
fmt.Println(s1.name, s1.sex, s1.age, s1.id, s1.addr)
//先定義變數
var s2 Student
s2.Person = new(Person)//new一下,分配空間
s2.name = "yoyo"
s2.sex = 'f'
s2.age = 22
s2.id = 2
s2.addr = "sz"
fmt.Println(s2.name, s2.sex, s2.age, s2.id, s2.addr)
}
二、方法
1.面向過程和物件函式的區別
//面向過程
func Add01(a, b int) int {
return a + b
}
type long int
//面向物件,方法:給某個型別繫結一個函式
//tmp叫接收者,接收者就是傳遞的一個引數
func (tmp long) Add02(other long) long {
return tmp + other
}
func main() {
var result int
result = Add01(1, 1)//普通函式呼叫方式
fmt.Println("result=", result)
//定義一個變數
var a long = 2
//呼叫方法格式:變數名.函式(所需函式)
res := a.Add02(3)
fmt.Println("res=", res)
//面向物件只是換了一種表現形式
}
2.為結構體型別新增方法
Person結構體定義如上
//帶有接收者的函式叫做方法
func (tmp Person) PrintInfo() {
fmt.Println("tmp=", tmp)
}
//通過一個函式給成員賦值
func (tmp *Person) SetInfo(n string, s byte, a int) {
tmp.name = n
tmp.sex = s
tmp.age = a
}
type long int
//只要接收者型別不同,即使方法同名也是不同的方法,不屬於重複定義
func (tmp long) test() {}
type char byte
func (tmp char) test() {}
type pointer *int
//pointer是指標,接收者型別本身不能是指標,接收者型別不能是指標
//func (tmp pointer) test() {}
func main() {
p := Person{"mike", 'm', 18}
p.PrintInfo()
var p2 Person
(&p2).SetInfo("yoyo", 'f', 22)
p2.PrintInfo()
}
3.值語義和引用
Person結構體的定義如上
//修改成員變數的值
//引數為普通變數,非指標,值語義,一份拷貝
func (p Person) SetInfoValue(n string, s byte, a int) {
p.name = n
p.sex = s
p.age = a
fmt.Println("p=", p)
fmt.Printf("SetInfoValue &p=%p\n", &p)
}
//接收者為指標變數,引用傳遞
func (p *Person) SetInfoPointer(n string, s byte, a int) {
p.name = n
p.sex = s
p.age = a
fmt.Printf("SetInfoPointer p=%p\n", p)
}
func main() {
var s1 Person = Person{"go", 'm', 22}
fmt.Printf("&s1=%p\n", &s1)
//值語義
s1.SetInfoValue("mike", 'm', 18)
fmt.Printf("s1=%+v\n", s1)
//引用語義
(&s1).SetInfoPointer("mike", 'm', 18)
fmt.Printf("s1=%+v\n", s1)
}
4.指標變數的方法集
Person結構體定義如上
func (p Person) SetInfoValue() {
fmt.Println("SetInfoValue")
}
func (p *Person) SetInfoPointer() {
fmt.Println("SetInfoPointer")
}
func main() {
//結構體變數是一個指標變數
//它能夠呼叫哪些方法,這些方法就是一個集合
//簡稱方法集
p := &Person{"mike", 'm', 18}
(*p).SetInfoPointer()
p.SetInfoPointer()
p.SetInfoValue()
(*p).SetInfoValue()
//p和*p之間互相轉化,然後呼叫方法
}
5.普通變數的方法集
func (p Person) SetInfoValue() {
fmt.Println("SetInfoValue")
}
func (p *Person) SetInfoPointer() {
fmt.Println("SetInfoPointer")
}
func main() {
p := Person{"mike", 'm', 18}
p.SetInfoPointer()
//內部先將p轉為&p再呼叫,(&p).SetInfoPointer
p.SetInfoValue()
}
6.方法的繼承
Person和Student結構體定義如上
//Person型別實現了一個方法
func (tmp *Person) PrintInfo() {
fmt.Printf("name=%s,sex=%c,age=%d\n", tmp.name, tmp.sex, tmp.age)
}
type Student struct{
Person
id int
sddr string
}
func main() {
//學生繼承了Person欄位,成員和方法都繼承了
s := Student{Person{"mike", 'm', 18}, 1, "bj"}
s.PrintInfo()
}
7.方法的重寫
Person和Student的結構體定義如上
func (tmp *Person) PrintInfo() {
fmt.Printf("name=%s,sex=%c,age=%d\n", tmp.name, tmp.sex, tmp.age)
}
//Student也實現了一個方法,這個方法和Person中的方法同名,這種叫做方法的重寫
func (tmp *Student) PrintInfo() {
fmt.Println("Student:tmp=", tmp)
}
func main() {
s := Student{Person{"mike", 'm', 18}, 1, "bj"}
//就近原則,先找本作用域的方法,找不到再找繼承的方法
s.PrintInfo()
//顯示呼叫繼承的方法
s.Person.PrintInfo()
}
8.方法值
func (p Person) SetInfoValue() {
fmt.Printf("SetInfoValue:%p,%v\n", &p, p)
}
func (p *Person) SetInfoPointer() {
fmt.Printf("SetInfoPointer:%p,%v\n", p, *p)
}
func main() {
s := Person{"mike", 'm', 18}
fmt.Printf("main:%p,%v", &s, s)
s.SetInfoPointer()//傳統呼叫方式
//儲存方法入口地址
sFunc := s.SetInfoPointer
//這個就是方法值,呼叫函式時,無需再傳遞接收者,隱藏了接收者
sFunc()
vFunc := s.SetInfoValue
vFunc()//等價於s.SetInfoValue()
}
9.方法表示式
Person結構體定義如上
func (p Person) SetInfoValue() {
fmt.Printf("SetInfoValue:%p,%v\n", &p, p)
}
func (p *Person) SetInfoPointer() {
fmt.Printf("SetInfoPointer:%p,%v\n", p, *p)
}
func main() {
p := Person{"mike", 'm', 18}
fmt.Printf("main:%p,%v\n", &p, p)
//方法值 f:=p.SetInfoPointer//隱藏了接收者
//方法表示式
f := (*Person).SetInfoPointer
f(&p) //顯示把接收者傳遞過去====》p.SetInfoPointer()
f2 := (Person).SetInfoValue
f2(p) //顯示把接收者傳遞過去====》p.SetInfoValue()
}
三、介面
1.介面的定義和實現
type Humaner interface {
//方法,只有宣告,沒有實現,由別的型別(自定義型別)實現
sayHi()
}
type Student struct {
name string
id int
}
func (tmp *Student) sayHi() {
fmt.Printf("Student[%s,%d] say hi\n", tmp.name, tmp.id)
}
type Teacher struct {
addr string
group string
}
func (tmp *Teacher) sayHi() {
fmt.Printf("Teacher[%s,%s] say hi\n", tmp.addr, tmp.group)
}
type Mystr string
func (tmp *Mystr) sayHi() {
fmt.Printf("Mystr[%s] say hi", *tmp)
}
func main() {
//定義介面型別變數
var i Humaner
//只要實現了此介面方法的型別,那麼這個型別的變數(接收者型別)就可賦值
s := &Student{"mike", 1}
i = s
i.sayHi()
t := &Teacher{"bj", "go"}
i = t
i.sayHi()
var str Mystr = "hello"
i = &str
i.sayHi()
}
2.多型
在1的基礎上,新增程式碼
//定義一個普通函式,函式的引數為介面型別
//只有一個函式,但可以有不同表現,多型
func WhoSayHi(i Humaner) {
i.sayHi()
}
func main() {
s := &Student{"mike", 1}
t := &Teacher{"bj", "go"}
var str Mystr = "hello"
//呼叫同一函式,實現不同表現,稱為多型
WhoSayHi(s)
WhoSayHi(t)
WhoSayHi(&str)
//建立一個切片
x := make([]Humaner, 3)
x[0] = s
x[1] = t
x[2] = &str
//第一個返回下標,第二個返回下標所對應的值
for _, i := range x {
i.sayHi()
}
}
3.介面的繼承
type Humaner interface {//子集
sayhi()
}
type personer interface {//超集
Humaner//匿名欄位,繼承了sayhi()方法
sing(lrc string)
}
type Student struct {
name string
id int
}
func (tmp *Student) sayhi() {
fmt.Printf("Student[%s,%d] say hi\n", tmp.name, tmp.id)
}
func (tmp *Student) sing(lrc string) {
fmt.Println("Student在唱:", lrc)
}
func main() {
//定義一個介面型別的變數
var i personer
s := &Student{"mike", 1}
i = s
i.sayhi()//繼承過來的方法
i.sing("lalala")
}
4.介面轉換
介面,結構體,方法定義如上
//超集可以轉換為子集,反過來不行
var iPro Personer//超集
iPro = &Student{"mike", 1}
var i Humaner//子集
//iPro=i//err
i = iPro
i.sayhi()
5.空介面
//空介面萬能型別,儲存任意型別的值
var i interface{} = 1
fmt.Println("i=", i)
i = "abc"
fmt.Println("i=", i)
6.型別斷言:if
type Student struct {
name string
id int
}
func main() {
i := make([]interface{}, 3)
i[0] = 1
i[1] = "hello"
i[2] = Student{"mike", 1}
//型別查詢,型別斷言
for index, data := range i {
//第一個返回的是值,第二個返回判斷結果的真假
if value, ok := data.(int); ok == true {
fmt.Printf("x[%d]型別為int,內容為%d\n", index, value)
} else if value, ok := data.(string); ok == true {
fmt.Printf("x[%d]型別為string,內容為%s\n", index, value)
} else if value, ok := data.(Student); ok == true {
fmt.Printf("x[%d]型別為Student,內容為name=%s,id=%d\n", index, value.name, value.id)
}
}
}
7.型別斷言:switch
func main() {
i := make([]interface{}, 3)
i[0] = 1
i[1] = "hello"
i[2] = Student{"mike", 1}
for index, data := range i {
switch value := data.(type) {//value還是值
case int:
fmt.Printf("x[%d]型別為int,內容為%d\n", index, value)
case string:
fmt.Printf("x[%d]型別為string,內容為%s\n", index, value)
case Student:
fmt.Printf("x[%d]型別為Student,內容為name=%s,id=%d\n", index, value.name, value.id)
}
}
}