自定義型別
在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的型別是int
。MyInt
型別只會在程式碼中存在,編譯完成時並不會有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 型別。