Go type關鍵字(類型定義與類型別名的使用差異)用法實例探究
1. 類型別名定義
定義類型別名的寫法為:
type TypeAlias = Type
類型別名規(guī)定:TypeAlias
只是 Type
的別名,本質(zhì)上 TypeAlias
與 Type
是同一個類型,就像一個孩子小時候有小名、乳名,上學后用學名,英語老師又會給他起英文名,但這些名字都指的是他本人。
2. 類型定義
類型定義語法如下:
type newType Type
其中 newType
是一種新的類型, newType
本身依然具備 Type
類型的特性。
類型聲明語句一般出現(xiàn)在包一級,因此如果新創(chuàng)建的類型名字的首字符大寫,則在包外部也可以使用。
一個類型聲明語句創(chuàng)建了一個新的類型名稱,和現(xiàn)有類型具有相同的底層結(jié)構。新命名的類型提供了一個方法,用來分隔不同概念的類型,這樣即使它們底層類型相同也是不兼容的。
為了說明類型聲明,我們將不同溫度單位分別定義為不同的類型:
package main type Celsius float64 // 攝氏溫度 type Fahrenheit float64 // 華氏溫度 const ( AbsoluteZeroC Celsius = -273.15 // 絕對零度 FreezingC Celsius = 0 // 結(jié)冰點溫度 BoilingC Celsius = 100 // 沸水溫度 ) func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) } func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
我們在這個包聲明了兩種類型:Celsius
和 Fahrenheit
分別對應不同的溫度單位。它們雖然有著相同的底層類型 float64
,但是它們是不同的數(shù)據(jù)類型,因此它們不可以被相互比較或混在一個表達式運算。
刻意區(qū)分類型,可以避免一些像無意中使用不同單位的溫度混合計算導致的錯誤;因此需要一個類似 Celsius(t)
或 Fahrenheit(t)
形式的顯式轉(zhuǎn)型操作才能將 float64
轉(zhuǎn)為對應的類型。
只有當兩個類型的底層基礎類型相同時,才允許這種轉(zhuǎn)型操作,或者是兩者都是指向相同底層結(jié)構的指針類型,這些轉(zhuǎn)換只改變類型而不會影響值本身。如果 x
是可以賦值給 T
類型的值,那么 x
必然也可以被轉(zhuǎn)為 T
類型,但是一般沒有這個必要。
底層數(shù)據(jù)類型決定了內(nèi)部結(jié)構和表達方式,也決定是否可以像底層類型一樣對內(nèi)置運算符的支持。這意味著, Celsius
和 Fahrenheit
類型的算術運算行為和底層的 float64
類型是一樣的,正如我們所期望的那樣。
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C</code><code>boilingF := CToF(BoilingC)</code><code>fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // "180" °F fmt.Printf("%g\n", boilingF-FreezingC) // compile error: type mismatch
比較運算符==
和<
也可以用來比較一個命名類型的變量和另一個有相同類型的變量,或有著相同底層類型的未命名類型的值之間做比較。但是如果兩個值有著不同的類型,則不能直接進行比較:
var c Celsius var f Fahrenheit fmt.Println(c == 0) // "true" fmt.Println(f >= 0) // "true" fmt.Println(c == f) // compile error: type mismatch fmt.Println(c == Celsius(f)) // "true"!
注意最后那個語句。盡管看起來像函數(shù)調(diào)用,但是 Celsius(f)
是類型轉(zhuǎn)換操作,它并不會改變值,僅僅是改變值的類型而已。測試為真的原因是因為 c
和 g
都是零值。
命名類型還可以為該類型的值定義新的行為。這些行為表示為一組關聯(lián)到該類型的函數(shù)集合,我們稱為類型的方法集。
下面的聲明語句, Celsius
類型的參數(shù) c
出現(xiàn)在了函數(shù)名的前面,表示聲明的是 Celsius
類型的一個名叫 String
的方法,該方法返回該類型對象 c
帶著 °C
溫度單位的字符串:
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
許多類型都會定義一個 String
方法,因為當使用 fmt
包的打印方法時,將會優(yōu)先使用該類型對應的 String
方法返回的結(jié)果打印。
c := FToC(212.0) fmt.Println(c.String()) // "100°C" fmt.Printf("%v\n", c) // "100°C"; no need to call String explicitly fmt.Printf("%s\n", c) // "100°C" fmt.Println(c) // "100°C" fmt.Printf("%g\n", c) // "100"; does not call String fmt.Println(float64(c)) // "100"; does not call String
3. 類型別名與類型定義差異
類型別名與類型定義表面上看只有一個等號的差異,那么它們之間實際的區(qū)別有哪些呢?下面通過一段代碼來理解。
package main import ( "fmt" ) // 將NewInt定義為int類型 // 通過 type 關鍵字的定義,NewInt 會形成一種新的類型,NewInt 本身依然具備 int 類型的特性。 type NewInt int // 將int取一個別名叫IntAlias, 將 IntAlias 設置為 int 的一個別名,使 IntAlias 與 int 等效。 type IntAlias = int func main() { // 將a聲明為NewInt類型 var a NewInt // 查看a的類型名 fmt.Printf("a type: %T\n", a) // a type: main.NewInt // 將 b 聲明為IntAlias類型 var b IntAlias // 查看b的類型名 fmt.Printf("b type: %T\n", b) // b type: int }
結(jié)果顯示 a 的類型是 main.NewInt
,表示 main 包下定義的 NewInt
類型,b 類型是 int
, IntAlias
類型只會在代碼中存在,編譯完成時,不會有 IntAlias
類型。
4. 非本地類型不能定義方法
能夠隨意地為各種類型起名字,是否意味著可以在自己包里為這些類型任意添加方法呢?參見下面的代碼演示:
package main import ( "time" ) // 定義time.Duration的別名為MyDuration type MyDuration = time.Duration // 為 MyDuration 添加一個方法 func (m MyDuration) EasySet(a string) { } func main() { }
錯誤信息:
./hello.go:11:6: cannot define new methods on non-local type time.Duration
編譯器提示:不能在一個非本地的類型 time.Duration
上定義新方法,非本地類型指的就是 time.Duration
不是在 main
包中定義的,而是在 time
包中定義的,與 main
包不在同一個包中,因此不能為不在一個包中的類型定義方法。修改方案為將第 8 行類型別名修改為類型定義,如下:
type MyDuration time.Duration
以上就是Go type關鍵字(類型定義與類型別名的使用差異)用法實例探究的詳細內(nèi)容,更多關于Go type關鍵字的資料請關注腳本之家其它相關文章!