Golang通脈之型別定義

發表於2021-10-25

自定義型別

在Go語言中有一些基本的資料型別,如string整型浮點型布林等資料型別, Go語言中可以使用type關鍵字來定義自定義型別。

type是Go語法裡的重要而且常用的關鍵字,type絕不只是對應於C/C++中的typedef。搞清楚type的使用,就容易理解go語言中的核心概念struct、interface、函式等的使用。

型別定義

定義結構體

使用type 可以定義結構體型別:

//1、定義結構體
//結構體定義
type person struct {
   name string //注意後面不能有逗號
   age  int
}

定義介面

使用type 可以定義介面型別:

type USB interface {
	start()
	end()
}

定義其他的新型別

使用type,還可以定義新型別。

語法:

type 型別名 Type

示例程式碼:

type myint int
type mystr string

func main() {

	 var i1 myint
	 var i2 = 100
	 i1 = 100
	 fmt.Println(i1)
	 //i1 = i2 //cannot use i2 (type int) as type myint in assignment
	 fmt.Println(i1,i2)
	 
	 var name mystr
	 name = "王二狗"
	 var s1 string
	 s1 = "李小花"
	 fmt.Println(name)
	 fmt.Println(s1)
	 name = s1 //cannot use s1 (type string) as type mystr in assignment
}

定義函式的型別

Go語言支援函數語言程式設計,可以使用高階程式設計語法。一個函式可以作為另一個函式的引數,也可以作為另一個函式的返回值,那麼在定義這個高階函式的時候,如果函式的型別比較複雜,可以使用type來定義這個函式的型別:

func main() {
	 res1 := fun1()
	 fmt.Println(res1(10,20))
}


type my_fun  func (int,int)(string)

//fun1()函式的返回值是my_func型別
func fun1 () my_fun{
	fun := func(a,b int) string {
		s := strconv.Itoa(a) + strconv.Itoa(b)
		return s
	}
	return fun
}

型別別名

型別別名規定:TypeAlias只是Type的別名,本質上TypeAlias與Type是同一個型別。就像一個孩子小時候有小名、乳名,上學後用學名,英語老師又會給他起英文名,但這些名字都指的是他本人。

型別別名是 Go 1.9 版本新增的新功能。主要用於程式碼升級、遷移中型別的相容性問題。在 C/C++語言中,程式碼重構升級可以使用巨集快速定義新的一段程式碼。Go 語言中沒有選擇加入巨集,而是將解決重構中最麻煩的型別名變更問題。

type TypeAlias = Type

在 Go 1.9 版本之前的內建型別定義的程式碼是這樣寫的:

type byte uint8
type rune int32

而在 Go 1.9 版本之後變為:

type byte = uint8
type rune = int32

這個修改就是配合型別別名而進行的修改。

型別定義和型別別名的區別

型別別名與型別定義表面上看只有一個等號的差異,我們通過下面的這段程式碼來理解它們之間的區別。

//型別定義
type NewInt int

//型別別名
type MyInt = int

func main() {
	var a NewInt
	var b MyInt
	
	fmt.Printf("type of a:%T\n", a) //type of a:main.NewInt
	fmt.Printf("type of b:%T\n", b) //type of b:int
}

結果顯示a的型別是main.NewInt,表示main包下定義的NewInt型別。b的型別是intMyInt型別只會在程式碼中存在,編譯完成時並不會有MyInt型別。

非本地型別不能定義方法

能夠隨意地為各種型別起名字,是否意味著可以在自己包裡為這些型別任意新增方法?

// 定義time.Duration的別名為MyDuration
type MyDuration = time.Duration
// 為MyDuration新增一個函式
func (m MyDuration) EasySet(a string) { //cannot define new methods on non-local type time.Duration
}
func main() {
}

以上程式碼報錯。報錯資訊:cannot define new methods on non-local type time.Duration

編譯器提示:不能在一個非本地的型別 time.Duration 上定義新方法。非本地方法指的就是使用 time.Duration 的程式碼所在的包,也就是 main 包。因為 time.Duration 是在 time 包中定義的,在 main 包中使用。time.Duration 包與 main 包不在同一個包中,因此不能為不在一個包中的型別定義方法。

解決這個問題有下面兩種方法:

  • 將型別別名改為型別定義: type MyDuration time.Duration,也就是將 MyDuration 從別名改為型別。
  • MyDuration 的別名定義放在 time 包中。

在結構體成員嵌入時使用別名

當型別別名作為結構體嵌入的成員時會發生什麼情況?

type Person struct {
	name string
}

func (p Person) Show() {
	fmt.Println("Person-->",p.name)
}

//型別別名
type People = Person

type Student struct {
	// 嵌入兩個結構
	Person
	People
}

func (p People) Show2(){
	fmt.Println("People------>",p.name)
}

func main() {
	//
	var s Student

	//s.name = "王二狗" //ambiguous selector s.name
	s.People.name = "李小花"
	s.Person.name = "王二狗"
	//s.Show() //ambiguous selector s.Show
	s.Person.Show()
	s.People.Show2()
	fmt.Printf("%T,%T\n",s.Person,s.People) //main.Person,main.Person

}

在通過s直接訪問name的時候,或者s直接呼叫Show()方法,因為兩個型別都有 name欄位和Show() 方法,會發生歧義,證明People 的本質確實是Person 型別。

相關文章