1. 程式人生 > >go語言基礎語法:面向物件程式設計

go語言基礎語法:面向物件程式設計

一、匿名組合

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)
		}
	}
}