天天看點

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

本文介紹SpringBoot相關内容。和【跨考菌】一起加油吧~

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

如果你有收獲,記得幫部落客一鍵三連哦😊

1 結構體

1.1 golang面向對象程式設計說明

  1. Golang 也支援面向對象程式設計(OOP), 但是和傳統的面向對象程式設計有差別, 并不是純粹的面向對

    象語言。 是以我們說 Golang 支援面向對象程式設計特性是比較準确的。

  2. Golang 沒有類(class), Go 語言的結構體(struct)和其它程式設計語言的類(class)有同等的地位, 你可以了解 Golang 是基于 struct 來實作 OOP 特性的。
  3. Golang 面向對象程式設計非常簡潔, 去掉了傳統 OOP 語言的繼承、 方法重載、 構造函數和析構函

    數、 隐藏的 this 指針等等

  4. Golang 仍然有面向對象程式設計的繼承, 封裝和多态的特性, 隻是實作的方式和其它 OOP 語言不

    一樣, 比如繼承 : Golang 沒有 extends 關鍵字, 繼承是通過匿名字段來實作。

  5. Golang 面向對象(OOP)很優雅, OOP 本身就是語言類型系統(type system)的一部分, 通過接口

    (interface)關聯, 耦合性低, 也非常靈活。 後面同學們會充分體會到這個特點。 也就是說在 Golang 中面向接口程式設計是非常重要的特性。

1.2 結構體和結構體變量的關系圖

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

說明:

  1. 将一類事物的特性提取出來(比如貓類), 形成一個新的資料類型, 就是一個結構體。
  2. 通過這個結構體, 我們可以建立多個變量(執行個體/對象)
  3. 事物可以貓類, 也可以是 Person , Fish 或是某個工具類。 。 。

1.3 簡單案例

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

1.4 結構體和結構體變量的差別和聯系

  1. 結構體是自定義的資料類型, 代表一類事物.
  2. 結構體變量(執行個體)是具體的, 實際的, 代表一個具體變量

1.5 結構體記憶體布局

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

1.6 如何聲明結構體

  • 基本文法
type 結構體名稱 struct {
	field1 type
	field2 type
}
           
  • 舉例:
type Student struct {
	Name string //字段
	Age int //字段
	Score float32
}
           

1.7 字段/屬性

1.7.1 基本介紹

  1. 從概念或叫法上看: 結構體字段 = 屬性 = field (即授課中, 統一叫字段)
  2. 字段是結構體的一個組成部分, 一般是基本資料類型、 數組,也可是引用類型。 比如我們前面定

    義貓結構體 的 Name string 就是屬性

注意事項和細節說明

  1. 字段聲明文法同變量, 示例:

    字段名 字段類型

  2. 字段的類型可以為: 基本類型、 數組或引用類型
  3. 在建立一個結構體變量後, 如果沒有給字段指派, 都對應一個零值(預設值), 規則同前面講的

    一樣:

    布爾類型是 false , 數值是 0 , 字元串是 “”。

    數組類型的預設值和它的元素類型相關, 比如 score [3]int 則為[0, 0, 0]

    指針, slice, 和 map 的零值都是 nil , 即還沒有配置設定空間。

    案例示範:

package main
import (
	"fmt"
)

//如果結構體的字段類型是: 指針,slice,和map的零值都是 nil ,即還沒有配置設定空間
//如果需要使用這樣的字段,需要先make,才能使用.

type Person struct{
	Name string
	Age int
	Scores [5]float64
	ptr *int //指針 
	slice []int //切片
	map1 map[string]string //map
}

type Monster struct{
	Name string
	Age int
}


func main() {

	//定義結構體變量
	var p1 Person
	fmt.Println(p1)

	if p1.ptr == nil {
		fmt.Println("ok1")
	}

	if p1.slice == nil {
		fmt.Println("ok2")
	}

	if p1.map1 == nil {
		fmt.Println("ok3")
	}

	//使用slice, 再次說明,一定要make
	p1.slice = make([]int, 10)
	p1.slice[0] = 100 //ok

	//使用map, 一定要先make
	p1.map1 = make(map[string]string)
	p1.map1["key1"] = "tom~" 
	fmt.Println(p1)
           
  1. 不同結構體變量的字段是獨立, 互不影響, 一個結構體變量字段的更改, 不影響另外一個, 結構體是值類型!!!

    案例:

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

1.8 建立結構體變量和通路結構體字段

方式 1-直接聲明

案例示範:

var person Person

方式 2-{}

案例示範:

var person Person = Person{}

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

方式 3-&

案例:

var person *Person = new (Person)

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

方式 4-{}

案例:

var person *Person = &Person{}

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

說明:

  1. 第 3 種和第 4 種方式傳回的是 結構體指針。
  2. 結構體指針通路字段的标準方式應該是:

    (*結構體指針).字段名

    , 比如

    (*person).Name = "tom"

  3. 但 go 做了一個簡化, 也支援 結構體指針.字段名, 比如 person.Name = “tom”。 更加符合程式員

    使用的習慣, go 編譯器底層 *對 person.Name 做了轉化 (person).Name。

1.9 struct類型的記憶體配置設定機制

  • 先來看一個思考題
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  • 基本說明
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  • 結構體在記憶體中示意圖
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  • 看下面的代碼,分析原因
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
    上述代碼記憶體分析:
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  • 分析下面代碼錯誤原因
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

1.10 結構體使用細節

  1. 結構體的所有字段在記憶體中是連續的
  2. 結構體是使用者單獨定義的類型, 和其它類型進行轉換時需要有完全相同的字段(名字、 個數和類

    型)

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  3. 結構體進行 type 重新定義(相當于取别名), Golang 認為是新的資料類型, 但是互相間可以強轉
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  4. struct 的每個字段上, 可以寫上一個 tag, 該 tag 可以通過反射機制擷取, 常見的使用場景就是序

    列化和反序列化。

  • 序列化的使用場景:
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

2 方法

2.1 基本介紹

在某些情況下, 我們要需要聲明(定義)方法。 比如 Person 結構體:除了有一些字段外( 年齡, 姓名…),Person 結構體還有一些行為,比如:可以說話、 跑步…,通過學習, 還可以做算術題。 這時就要用方法才能完成。

Golang 中的方法是作用在指定的資料類型上的(即: 和指定的資料類型綁定), 是以自定義類型,都可以有方法, 而不僅僅是 struct。

2.2 方法的聲明和調用

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

文法說明:

  1. func (a A) test() {} 表示 A 結構體有一方法, 方法名為 test
  2. (a A) 展現 test 方法是和 A 類型綁定的

舉例說明:

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

總結:

  1. test 方法和 Person 類型綁定
  2. test 方法隻能通過 Person 類型的變量來調用, 而不能直接調用, 也不能使用其它類型變量來調

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  3. func (p Person) test() {}… p 表示哪個 Person 變量調用, 這個 p 就是它的副本, 這點和函數傳參非

    常相似。

  4. p 這個名字, 有程式員指定, 不是固定, 比如修改成 person 也是可以
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

2.3 方法入門

  1. 給 Person 結構體添加 speak 方法,輸出 xxx 是一個好人
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  2. 給 Person 結構體添加 jisuan 方法,可以計算從 1+…+1000 的結果, 說明方法體内可以函數一樣,

    進行各種運算

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  3. 給 Person 結構體 jisuan2 方法,該方法可以接收一個數 n, 計算從 1+…+n 的結果
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  4. 給 Person 結構體添加 getSum 方法,可以計算兩個數的和, 并傳回結果
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  5. 方法的調用
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

2.4 方法的聲明/定義

func (recevier type) methodName(參數清單) (傳回值清單){
	方法體
	return 傳回值
}
           
  1. 參數清單: 表示方法輸入
  2. recevier type : 表示這個方法和 type 這個類型進行綁定, 或者說該方法作用于 type 類型
  3. receiver type : type 可以是結構體, 也可以其它的自定義類型
  4. receiver : 就是 type 類型的一個變量(執行個體), 比如 : Person 結構體 的一個變量(執行個體)
  5. 傳回值清單: 表示傳回的值, 可以多個
  6. 方法主體: 表示為了實作某一功能代碼塊
  7. return 語句不是必須的

2.5 使用細節

  1. 結構體類型是值類型, 在方法調用中, 遵守值類型的傳遞機制, 是值拷貝傳遞方式
  2. 如程式員希望在方法中, 修改結構體變量的值, 可以通過結構體指針的方式來處理
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  3. Golang 中的方法作用在指定的資料類型上的(即: 和指定的資料類型綁定), 是以自定義類型,

    都可以有方法, 而不僅僅是 struct, 比如 int , float32 等都可以有方法

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  4. 方法的通路範圍控制的規則, 和函數一樣。 方法名首字母小寫, 隻能在本包通路, 方法首字母

    大寫, 可以在本包和其它包通路。 [講解]

  5. 如果一個類型實作了 String()這個方法, 那麼 fmt.Println 預設會調用這個變量的 String()進行輸出
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

2.6 方法和函數的差別

1) 調用方式不一樣

函數的調用方式:

函數名(實參清單)

方法的調用方式:

變量.方法名(實參清單)

2) 對于普通函數, 接收者為值類型時, 不能将指針類型的資料直接傳遞, 反之亦然

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

3) 對于方法(如 struct 的方法) , 接收者為值類型時, 可以直接用指針類型的變量調用方法, 反

過來同樣也可以

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

總結:

  1. 不管調用形式如何, 真正決定是值拷貝還是位址拷貝, 看這個方法是和哪個類型綁定.
  2. *如果是和值類型, 比如 (p Person) , 則是值拷貝, 如果和指針類型, 比如是 (p Person) 則

    是位址拷貝。

3 工廠模式

3.1 說明

Golang 的結構體沒有構造函數, 通常可以使用工廠模式來解決這個問題。

3.2 先看一個需求

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

3.3 工廠模式如何解決這個問題呢?

使用工廠模式實作跨包建立結構體執行個體(變量)的案例:

如果 model 包的 結構體變量首字母大寫, 引入後, 直接使用, 沒有問題

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

如果 model 包的 結構體變量首字母小寫, 引入後, 不能直接使用, 可以工廠模式解決, 看代碼:

student.go

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

main.go

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

3.4 思考

如果 model 包的 student 的結構體的字段 Score 改成 score, 我們還能正常通路

嗎? 又應該如何解決這個問題呢?

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

4 面向對象程式設計三大特性-封裝

4.1 封裝的好處

  1. 隐藏實作細節
  2. 提可以對資料進行驗證, 保證安全合理(Age)

如何展現封裝呢?

  1. 對結構體中的屬性進行封裝
  2. 通過方法, 包 實作封裝

4.2 封裝實作步驟

  1. 将結構體、 字段(屬性)的首字母小寫(不能導出了, 其它包不能使用, 類似 private)
  2. 給結構體所在包提供一個工廠模式的函數, 首字母大寫。 類似一個構造函數
  3. 提供一個首字母大寫的 Set 方法(類似其它語言的 public), 用于對屬性判斷并指派
func (var 結構體類型名) SetXxx(參數清單) (傳回值清單) {
	//加入資料驗證的業務邏輯
	var.字段 = 參數
} 
           
  1. 提供一個首字母大寫的 Get 方法(類似其它語言的 public), 用于擷取屬性的值
func (var 結構體類型名) GetXxx() {
	return var.age;
}
           

特别說明: 在 Golang 開發中并沒有特别強調封裝, 這點并不像 Java. 是以提醒學過 java 的朋友,不用總是用 java 的文法特性來看待 Golang, Golang 本身對面向對象的特性做了簡化的.

4.3 簡單案例

請大家看一個程式(person.go),不能随便檢視人的年齡,工資等隐私, 并對輸入的年齡進行合理的驗證。 設計: model 包(person.go) main 包(main.go 調用 Person 結構體)

model/person.go

package model
import "fmt"

type person struct {
	Name string
	age int   //其它包不能直接通路..
	sal float64
}

//寫一個工廠模式的函數,相當于構造函數
func NewPerson(name string) *person {
	return &person{
		Name : name,
	}
}

//為了通路age 和 sal 我們編寫一對SetXxx的方法和GetXxx的方法
func (p *person) SetAge(age int) {
	if age >0 && age <150 {
		p.age = age
	} else {
		fmt.Println("年齡範圍不正确..")
		//給程式員給一個預設值
	}
}

func (p *person) GetAge() int {
	return p.age
}


func (p *person) SetSal(sal float64) {
	if sal >= 3000 && sal <= 30000 {
		p.sal = sal
	} else {
		fmt.Println("薪水範圍不正确..")
		
	}
}

func (p *person) GetSal() float64 {
	return p.sal
}

           

main/main.go

package main
import (
	"fmt"
	"test/chapter11/encapsulate/model"
)

func main() {

	p := model.NewPerson("smith")
	p.SetAge(18)
	p.SetSal(5000)
	fmt.Println(p)
	fmt.Println(p.Name, " age =", p.GetAge(), " sal = ", p.GetSal())
	
}
           

4.4 案例2

  1. 建立程式,在 model 包中定義 Account 結構體: 在 main 函數中體會 Golang 的封裝性。
  2. Account 結構體要求具有字段: 賬号(長度在 6-10 之間) 、 餘額(必須>20)、 密碼(必須是六
  3. 通過 SetXxx 的方法給 Account 的字段指派。
  4. 在 main 函數中測試

model/account.go

package model

import (
	"fmt"
)
//定義一個結構體account
type account struct {
	accountNo string
	pwd string
	balance float64
}

//工廠模式的函數-構造函數
func NewAccount(accountNo string, pwd string, balance float64) *account {

	if len(accountNo) < 6 || len(accountNo) > 10 {
		fmt.Println("賬号的長度不對...")
		return nil
	}

	if len(pwd) != 6 {
		fmt.Println("密碼的長度不對...")
		return nil
	}

	if balance < 20 {
		fmt.Println("餘額數目不對...")
		return nil
	}

	return &account{
		accountNo : accountNo,
		pwd : pwd,
		balance : balance,
	}

}

//方法
//1. 存款
func (account *account) Deposite(money float64, pwd string)  {

	//看下輸入的密碼是否正确
	if pwd != account.pwd {
		fmt.Println("你輸入的密碼不正确")
		return 
	}

	//看看存款金額是否正确
	if money <= 0 {
		fmt.Println("你輸入的金額不正确")
		return 
	}

	account.balance += money
	fmt.Println("存款成功~~")

}

//取款
func (account *account) WithDraw(money float64, pwd string)  {

	//看下輸入的密碼是否正确
	if pwd != account.pwd {
		fmt.Println("你輸入的密碼不正确")
		return 
	}

	//看看取款金額是否正确
	if money <= 0  || money > account.balance {
		fmt.Println("你輸入的金額不正确")
		return 
	}

	account.balance -= money
	fmt.Println("取款成功~~")

}

//查詢餘額
func (account *account) Query(pwd string)  {

	//看下輸入的密碼是否正确
	if pwd != account.pwd {
		fmt.Println("你輸入的密碼不正确")
		return 
	}

	fmt.Printf("你的賬号為=%v 餘額=%v \n", account.accountNo, account.balance)

}
           

main/main.go

package main

import (
	"fmt"
	"test/chapter11/encapexercise/model"
)

func main() {
	//建立一個account變量
	account := model.NewAccount("jzh11111", "000", 40)
	if account != nil {
		fmt.Println("建立成功=", account)
	} else {
		fmt.Println("建立失敗")
	}
}
           

5 面向對象程式設計三大特性-繼承

5.1 為什麼需要繼承呢?

先看一個例子:

看個學生考試系統的程式 extends01.go, 提出代碼複用的問題

package main

import (
	"fmt"
)




//編寫一個學生考試系統

type Student struct {
	Name string
	Age int
	Score int
}

//将Pupil 和 Graduate 共有的方法也綁定到 *Student
func (stu *Student) ShowInfo() {
	fmt.Printf("學生名=%v 年齡=%v 成績=%v\n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) SetScore(score int) {
	//業務判斷
	stu.Score = score
}

//給 *Student 增加一個方法,那麼 Pupil 和 Graduate都可以使用該方法
func (stu *Student) GetSum(n1 int, n2 int) int {
	return n1 + n2
}

//國小生
type Pupil struct { 
	Student //嵌入了Student匿名結構體
}

//顯示他的成績

//這時Pupil結構體特有的方法,保留
func (p *Pupil) testing() {
	fmt.Println("國小生正在考試中.....")
}

//大學生, 研究所學生。。


//大學生
type Graduate struct {
	Student //嵌入了Student匿名結構體
}

//顯示他的成績
//這時Graduate結構體特有的方法,保留
func (p *Graduate) testing() {
	fmt.Println("大學生正在考試中.....")
}

//代碼備援.. 高中生....

func main() {

	//當我們對結構體嵌入了匿名結構體使用方法會發生變化
	pupil := &Pupil{}
	pupil.Student.Name = "tom~"
	pupil.Student.Age = 8
	pupil.testing() 
	pupil.Student.SetScore(70)
	pupil.Student.ShowInfo()
	fmt.Println("res=", pupil.Student.GetSum(1, 2))


	graduate := &Graduate{}
	graduate.Student.Name = "mary~"
	graduate.Student.Age = 28
	graduate.testing() 
	graduate.Student.SetScore(90)
	graduate.Student.ShowInfo()
	fmt.Println("res=", graduate.Student.GetSum(10, 20))
}
           

對上面代碼的小結

  1. Pupil 和 Graduate 兩個結構體的字段和方法幾乎, 但是我們卻寫了相同的代碼, 代碼複用性不

  2. 出現代碼備援, 而且代碼不利于維護, 同時也不利于功能的擴充。
  3. 解決方法-通過繼承方式來解決

5.2 繼承的示意圖

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

也就是說: 在 Golang 中, 如果一個 struct 嵌套了另一個匿名結構體, 那麼這個結構體可以直接通路匿名結構體的字段和方法, 進而實作了繼承特性。

5.3 嵌套匿名構造體的基本文法

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

5.4 使用案例

對上述備援的代碼利用繼承來改進:

package main

import (
	"fmt"
)




//編寫一個學生考試系統

type Student struct {
	Name string
	Age int
	Score int
}

//将Pupil 和 Graduate 共有的方法也綁定到 *Student
func (stu *Student) ShowInfo() {
	fmt.Printf("學生名=%v 年齡=%v 成績=%v\n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) SetScore(score int) {
	//業務判斷
	stu.Score = score
}

//給 *Student 增加一個方法,那麼 Pupil 和 Graduate都可以使用該方法
func (stu *Student) GetSum(n1 int, n2 int) int {
	return n1 + n2
}

//國小生
type Pupil struct { 
	Student //嵌入了Student匿名結構體
}

//顯示他的成績

//這時Pupil結構體特有的方法,保留
func (p *Pupil) testing() {
	fmt.Println("國小生正在考試中.....")
}

//大學生, 研究所學生。。


//大學生
type Graduate struct {
	Student //嵌入了Student匿名結構體
}

//顯示他的成績
//這時Graduate結構體特有的方法,保留
func (p *Graduate) testing() {
	fmt.Println("大學生正在考試中.....")
}

//代碼備援.. 高中生....

func main() {

	//當我們對結構體嵌入了匿名結構體使用方法會發生變化
	pupil := &Pupil{}
	pupil.Student.Name = "tom~"
	pupil.Student.Age = 8
	pupil.testing() 
	pupil.Student.SetScore(70)
	pupil.Student.ShowInfo()
	fmt.Println("res=", pupil.Student.GetSum(1, 2))


	graduate := &Graduate{}
	graduate.Student.Name = "mary~"
	graduate.Student.Age = 28
	graduate.testing() 
	graduate.Student.SetScore(90)
	graduate.Student.ShowInfo()
	fmt.Println("res=", graduate.Student.GetSum(10, 20))
}
           

發現,繼承給程式設計帶來下面的便利:

  1. 代碼的複用性提高了
  2. 代碼的擴充性和維護性提高了

5.5 繼承的深入

  1. 結構體可以使用嵌套匿名結構體所有的字段和方法, 即: 首字母大寫或者小寫的字段、 方法,

    都可以使用。 【舉例說明】

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  2. 匿名結構體字段通路可以簡化, 如圖
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

    對上面的代碼小結

    (1) 當我們直接通過 b 通路字段或方法時, 其執行流程如下比如 b.Name

    (2) 編譯器會先看 b 對應的類型有沒有 Name, 如果有, 則直接調用 B 類型的 Name 字段

    (3) 如果沒有就去看 B 中嵌入的匿名結構體 A 有沒有聲明 Name 字段, 如果有就調用,如果沒有

    繼續查找…如果都找不到就報錯.

  3. 當結構體和匿名結構體有相同的字段或者方法時, 編譯器采用就近通路原則通路, 如希望通路

    匿名結構體的字段和方法, 可以通過匿名結構體名來區分【舉例說明】

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  4. 結構體嵌入兩個(或多個)匿名結構體, 如兩個匿名結構體有相同的字段和方法(同時結構體本身

    沒有同名的字段和方法), 在通路時, 就必須明确指定匿名結構體名字, 否則編譯報錯。 【舉例說明】

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  5. 如果一個 struct 嵌套了一個有名結構體, 這種模式就是組合, 如果是組合關系, 那麼在通路組合的結構體的字段或方法時, 必須帶上結構體的名字
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  6. 嵌套匿名結構體後, 也可以在建立結構體變量(執行個體)時, 直接指定各個匿名結構體字段的值
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

5.6 案例剖析

結構體的匿名字段是基本資料類型, 如何通路, 下面代碼輸出什麼

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

說明

  1. 如果一個結構體有 int 類型的匿名字段, 就不能第二個。
  2. 如果需要有多個 int 的字段, 則必須給 int 字段指定名字

5 面向對象程式設計三大特性-多重繼承

  • 多重繼承說明

    如一個 struct 嵌套了多個匿名結構體, 那麼該結構體可以直接通路嵌套的匿名結構體的字段和方

    法, 進而實作了多重繼承。

  • 案例示範

    通過一個案例來說明多重繼承使用

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
    多重繼承細節說明
  1. 如嵌入的匿名結構體有相同的字段名或者方法名, 則在通路時, 需要通過匿名結構體類型名來

    區分。 【案例示範】

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  2. 為了保證代碼的簡潔性, 建議大家盡量不使用多重繼承

6 接口

在 Golang 中 多态特性主要是通過接口來展現的。

6.1 接口入門案例

package main
import (
	"fmt"
)

//聲明/定義一個接口
type Usb interface {
	//聲明了兩個沒有實作的方法
	Start()
	Stop()
}

type Phone struct {
	name string
}  

//讓Phone 實作 Usb接口的方法
func (p Phone) Start() {
	fmt.Println("手機開始工作。。。")
}
func (p Phone) Stop() {
	fmt.Println("手機停止工作。。。")
}

func (p Phone) Call() {
	fmt.Println("手機 在打電話..")
}


type Camera struct {
	name string
}
//讓Camera 實作   Usb接口的方法
func (c Camera) Start() {
	fmt.Println("相機開始工作。。。")
}
func (c Camera) Stop() {
	fmt.Println("相機停止工作。。。")
}

type Computer struct {

}

func (computer Computer) Working(usb Usb) {
	usb.Start()
	//如果usb是指向Phone結構體變量,則還需要調用Call方法
	//類型斷言..[注意體會!!!]
	if phone, ok := usb.(Phone); ok {
		phone.Call()
	}
	usb.Stop()
}

func main() {
	//定義一個Usb接口數組,可以存放Phone和Camera的結構體變量
	//這裡就展現出多态數組
	var usbArr [3]Usb
	usbArr[0] = Phone{"vivo"}
	usbArr[1] = Phone{"小米"}
	usbArr[2] = Camera{"尼康"}

	//周遊usbArr
	//Phone還有一個特有的方法call(),請周遊Usb數組,如果是Phone變量,
	//除了調用Usb 接口聲明的方法外,還需要調用Phone 特有方法 call. =》類型斷言
	var computer Computer
	for _, v := range usbArr{
		computer.Working(v)
		fmt.Println()
	}
	//fmt.Println(usbArr)
}
           

6.2 接口基本文法

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

小結說明:

  1. 接口裡的所有方法都沒有方法體, 即接口的方法都是沒有實作的方法。 接口展現了程式設計的

    多态和高内聚低偶合的思想。

  2. Golang 中的接口, 不需要顯式的實作。 隻要一個變量, 含有接口類型中的所有方法, 那麼這個

    變量就實作這個接口。 是以, Golang 中沒有 implement 這樣的關鍵字

6.3 注意事項和細節

  1. 接口本身不能建立執行個體,但是可以指向一個實作了該接口的自定義類型的變量(執行個體)
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  2. 接口中所有的方法都沒有方法體,即都是沒有實作的方法。
  3. 在 Golang 中, 一個自定義類型需要将某個接口的所有方法都實作, 我們說這個自定義類型實作

    了該接口。

  4. 一個自定義類型隻有實作了某個接口, 才能将該自定義類型的執行個體(變量)賦給接口類型
  5. 隻要是自定義資料類型, 就可以實作接口, 不僅僅是結構體類型。
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

6) 一個自定義類型可以實作多個接口

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

7) Golang 接口中不能有任何變量

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

8) 一個接口(比如 A 接口)可以繼承多個别的接口(比如 B,C 接口), 這時如果要實作 A 接口, 也必須将 B,C 接口的方法也全部實作。

package main
import (
	"fmt"
)

type BInterface interface {
	test01()
}

type CInterface interface {
	test02()
}

type AInterface interface {
	BInterface
	CInterface
	test03()
}

//如果需要實作AInterface,就需要将BInterface CInterface的方法都實作
type Stu struct {
}
func (stu Stu) test01() {

}
func (stu Stu) test02() {
	
}
func (stu Stu) test03() {
	
}
func main() {
	var stu Stu
	var a AInterface = stu
	a.test01()
}
           
  1. interface 類型預設是一個指針(引用類型), 如果沒有對 interface 初始化就使用, 那麼會輸出 nil
  2. 空接口 interface{} 沒有任何方法, 是以所有類型都實作了空接口, 即我們可以把任何一個變量

    賦給空接口。

type T  interface{

}
           
var t T = stu //ok
fmt.Println(t)
var t2 interface{}  = stu
var num1 float64 = 8.8
t2 = num1
t = num1
fmt.Println(t2, t)
           

6.4 案例

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

6.5 案例:實作對 Hero 結構體切片的排序: sort.Sort(data Interface)

package main
import (
	"fmt"
	"sort"
	"math/rand"
)

//1.聲明Hero結構體
type  Hero struct{
	Name string
	Age int
}

//2.聲明一個Hero結構體切片類型
type HeroSlice []Hero

//3.實作Interface 接口
func (hs HeroSlice) Len() int {
	return len(hs)
}

//Less方法就是決定你使用什麼标準進行排序
//1. 按Hero的年齡從小到大排序!!
func (hs HeroSlice) Less(i, j int) bool {
	return hs[i].Age < hs[j].Age
	//修改成對Name排序
	//return hs[i].Name < hs[j].Name
}

func (hs HeroSlice) Swap(i, j int) {
	//交換
	// temp := hs[i]
	// hs[i] = hs[j]
	// hs[j] = temp
	//下面的一句話等價于三句話
	hs[i], hs[j] = hs[j], hs[i]
}


//1.聲明Student結構體
type  Student struct{
	Name string
	Age int
	Score float64
}

//将Student的切片,安Score從大到小排序!!

func main() {

	//先定義一個數組/切片
	var intSlice = []int{0, -1, 10, 7, 90}
	//要求對 intSlice切片進行排序
	//1. 冒泡排序...
	//2. 也可以使用系統提供的方法 
	sort.Ints(intSlice) 
	fmt.Println(intSlice)

	//請大家對結構體切片進行排序
	//1. 冒泡排序...
	//2. 也可以使用系統提供的方法

	//測試看看我們是否可以對結構體切片進行排序
	var heroes HeroSlice
	for i := 0; i < 10 ; i++ {
		hero := Hero{
			Name : fmt.Sprintf("英雄|%d", rand.Intn(100)),
			Age : rand.Intn(100),
		}
		//将 hero append到 heroes切片
		heroes = append(heroes, hero)
	}

	//看看排序前的順序
	for _ , v := range heroes {
		fmt.Println(v)
	}

	//調用sort.Sort
	sort.Sort(heroes)
	fmt.Println("-----------排序後------------")
	//看看排序後的順序
	for _ , v := range heroes {
		fmt.Println(v)
	}

	i := 10
	j := 20
	i, j = j, i
	fmt.Println("i=", i, "j=", j) // i=20 j = 10
}
           

7 實作接口vs繼承

package main
import (
	"fmt"
)

//Monkey結構體
type Monkey struct {
	Name string
}

//聲明接口
type BirdAble interface {
	Flying()
}

type FishAble interface {
	Swimming()
}

func (this *Monkey) climbing() {
	fmt.Println(this.Name, " 生來會爬樹..")
}

//LittleMonkey結構體
type LittleMonkey struct {
	Monkey //繼承
}


//讓LittleMonkey實作BirdAble
func (this *LittleMonkey) Flying() {
	fmt.Println(this.Name, " 通過學習,會飛翔...")
}

//讓LittleMonkey實作FishAble
func (this *LittleMonkey) Swimming() {
	fmt.Println(this.Name, " 通過學習,會遊泳..")
}

func main() {

	//建立一個LittleMonkey 執行個體
	monkey := LittleMonkey{
		Monkey {
			Name : "悟空",
		},
	}
	monkey.climbing()
	monkey.Flying()
	monkey.Swimming()

}
           

代碼說明:

  1. 當 A 結構體繼承了 B 結構體, 那麼 A 結構就自動的繼承了 B 結構體的字段和方法, 并且可以直

    接使用

  2. 當 A 結構體需要擴充功能, 同時不希望去破壞繼承關系, 則可以去實作某個接口即可, 是以我

    們可以認為: 實作接口是對繼承機制的補充.

    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  • 接口和繼承解決的解決的問題不同

    繼承的價值主要在于: 解決代碼的複用性和可維護性。

    接口的價值主要在于: 設計, 設計好各種規範(方法), 讓其它自定義類型去實作這些方法。

  • 接口比繼承更加靈活

    Person Student BirdAble LittleMonkey

    接口比繼承更加靈活, 繼承是滿足 is - a 的關系, 而接口隻需滿足 like - a 的關系。
  • 接口在一定程度上實作代碼解耦

8 類型斷言

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

8.1 基本介紹

類型斷言, 由于接口是一般類型, 不知道具體類型, 如果要轉成具體類型, 就需要使用類型斷言,具體的如下:

【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言
  • 對上面代碼的說明:

    在進行類型斷言時, 如果類型不比對, 就會報 panic, 是以進行類型斷言時, 要確定原來的空接口指向的就是斷言的類型.

  • 如何在進行斷言時, 帶上檢測機制, 如果成功就 ok,否則也不要報 panic
    【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

8.2 類型斷言案例1

在前面的 Usb 接口案例做改進:

給 Phone 結構體增加一個特有的方法 call(), 當 Usb 接口接收的是 Phone 變量時, 還需要調用 call

方法, 走代碼:

func (computer Computer) Working(usb Usb) {
	usb.Start()
	//如果usb是指向Phone結構體變量,則還需要調用Call方法
	//類型斷言..[注意體會!!!]
	if phone, ok := usb.(Phone); ok {
		phone.Call()
	}
	usb.Stop()
}
           
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言

8.2 類型斷言案例2

寫一函數, 循環判斷傳入參數的類型:

package main
import (
	"fmt"
)


//定義Student類型
type Student struct {

}

//編寫一個函數,可以判斷輸入的參數是什麼類型
func TypeJudge(items... interface{}) {
	for index, x := range items {
		switch x.(type) {
			case bool :
				fmt.Printf("第%v個參數是 bool 類型,值是%v\n", index, x)
			case float32 :
				fmt.Printf("第%v個參數是 float32 類型,值是%v\n", index, x)
			case float64 :
				fmt.Printf("第%v個參數是 float64 類型,值是%v\n", index, x)
			case int, int32, int64 :
				fmt.Printf("第%v個參數是 整數 類型,值是%v\n", index, x)
			case string :
				fmt.Printf("第%v個參數是 string 類型,值是%v\n", index, x)
			case Student :
				fmt.Printf("第%v個參數是 Student 類型,值是%v\n", index, x)
			case *Student :
				fmt.Printf("第%v個參數是 *Student 類型,值是%v\n", index, x)
			default :
				fmt.Printf("第%v個參數是  類型 不确定,值是%v\n", index, x)
		}
	}
}




func main() {

	var n1 float32 = 1.1
	var n2 float64 = 2.3
	var n3 int32 = 30
	var name string = "tom"
	address := "北京"
	n4 := 300

	stu1 := Student{}
	stu2 := &Student{}

	TypeJudge(n1, n2, n3, name, address, n4, stu1, stu2)


}
           
【golang學習總結】11 golang面向對象程式設計1 結構體2 方法3 工廠模式4 面向對象程式設計三大特性-封裝5 面向對象程式設計三大特性-繼承5 面向對象程式設計三大特性-多重繼承6 接口7 實作接口vs繼承8 類型斷言