type是go語法裡的重要且常用的關鍵字,type絕不只是對應於C/C 中的typedef。 type又有兩種使用方式,一種是型別別名,一種是型別定義,是不是熟悉的C語言(define和typedef)味道?
類型定義
type Student struct { name String age int } type I int
類型別名
type Sdt = Student type I = int
type有下列幾種用法:
定義結構體
定義介面
類型定義
類型別名
類型查詢
定義結構體
結構體是使用者自訂的一種抽象的資料結構,golang中struct類似於java語言中的class, 在程式設計中,有著舉足輕重的地位。結構體的用法,將會在struct關鍵字中詳細的介紹。下邊來看定義一個結構體的語法格式:
type name struct { Field1 dataType Field2 dataType Field3 dataType }
定義介面
介面相關知識點,將會在interface關鍵字中詳細介紹,下邊來看一段定義介面的語法格式:
type name interface{ Read() Write() }
類型定義
使用類型定義定義出來的類型與原始類型不相同,所以不能使用新類型變數賦值給原型別變量,除非使用強制型別轉換。下面來看一段範例程式碼,根據string類型,定義一種新的類型,新類型名稱是name:
type name string
為什麼要使用類型定義呢?
類型定義可以在原始類型的基礎上創造出新的類型,有些場合下可以使程式碼更簡潔,如下邊範例程式碼:
package main import ( "fmt" ) // 定义一个接收一个字符串类型参数的函数类型 type handle func(str string) // exec函数,接收handle类型的参数 func exec(f handle) { f("hello") } func main() { // 定义一个函数类型变量,这个函数接收一个字符串类型的参数 var p = func(str string) { fmt.Println("first", str) } exec(p) // 匿名函数作为参数直接传递给exec函数 exec(func(str string) { fmt.Println("second", str) }) }
輸出資訊是:
first hello second hello
上邊的範例是類型定義的一種簡單應用場合,如果不使用類型定義,那麼想要實現上邊範例中的功能,應該怎麼書寫這段程式碼呢?
// exec函数,接收handle类型的参数 func exec(f func(str string)) { f("hello") }
exec函數中的參數類型,要替換成func(str string)了,咋一看去也不複雜,但是假如exec接收一個需要5個參數的函數變數呢?是不是感覺參數列表就會很久了。
func exec(f func(str string, str2 string, num int, money float64, flag bool)) { f("hello") }
從上邊的程式碼可以發現,exec函數的參數清單可讀性變差了。下邊再來看看使用類型定義是怎麼實作這個功能:
package main import ( "fmt" ) // 定义一个需要五个参数的函数类型 type handle func(str string, str2 string, num int, money float64, flag bool) // exec函数,接收handle类型的参数 func exec(f handle) { f("hello", "world", 10, 11.23, true) } func demo(str string, str2 string, num int, money float64, flag bool) { fmt.Println(str, str2, num, money, flag) } func main() { exec(demo) }
類型別名
類型別名這個特性在golang1.9中引入。使用型別別名定義出來的型別與原型別一樣,也就是可以與原型別變數互相賦值,又擁有了原型別的所有方法集。給strng類型取一個別名,別名名稱是name:
type name = string
類型別名與型別定義不同之處在於,使用型別別名需要在別名與原型別之間加上賦值符號(=);使用型別定義別名定義的類型與原型別等價,而使用型別定義出來的型別是一種新的型別。
如下邊範例:
package main import ( "fmt" ) type a = string type b string func SayA(str a) { fmt.Println(str) } func SayB(str b) { fmt.Println(str) } func main() { var str = "test" SayA(str) //错误参数传递,str是字符串类型,不能赋值给b类型变量 SayB(str) }
這段程式碼在編譯時會出現如下錯誤:
.\main.go:21:6: cannot use str (type string) as type b in argument to SayB
從錯誤訊息可知,str為字串類型,不能當做b類型參數傳入SayB函數中。而str卻可以當做a型別參數傳入到SayA函數中。由此可見,使用型別別名定義的型別與原型別一致,而型別定義定義出來的型別,是一種新的型別。
新增方法給型別方法,會加入到原始型別方法集中
給型別別方法後,原型別也能使用這個方法。下邊請看一段範例程式碼:
package main import ( "fmt" ) // 根据string类型,定义类型S type S string func (r *S) Hi() { fmt.Println("S hi") } // 定义S的类型别名为T type T = S func (r *T) Hello() { fmt.Println("T hello") } // 函数参数接收S类型的指针变量 func exec(obj *S) { obj.Hello() obj.Hi() } func main() { t := new(T) s := new(S) exec(s) // 将T类型指针变量传递给S类型指针变量 exec(t) }
輸出資訊是:
T hello S hi T hello S hi
上邊的範例中,S是原型,T是S型別別名。在給T增加了Hello方法後,S類型的變數也可以使用Hello方法。說明給型別別名新增方法後,原型別也能使用這個方法。從範例中可知,變數t可以賦值給S型別變數s,所以型別別名是給原型別取了一個小名,本質上沒有發生任何變化。
型別別名,只能對同一個套件中的自訂型別產生作用。舉個例子,golang sdk中有很多個包,是不是我們可以使用型別別名,為sdk包中的結構體型別新增方法呢?答案是:不行。請牢記一點:類型別名,只能對包內的類型產生作用,對包外的類型採用類型別名,在編譯時將會提示如下資訊:
cannot define new methods on non-local type string
類型查詢
類型查詢,就是根據變量,查詢這個變數的類型。為什麼會有這樣的需求呢? goalng中有一個特殊的類型interface{},這個類型可以被任何類型的變量賦值,如果想要知道到底是哪個類型的變量賦值給了interface{}類型變量,就需要使用類型查詢來解決這個需求,範例程式碼如下:
package main import ( "fmt" ) func main() { // 定义一个interface{}类型变量,并使用string类型值”abc“初始化 var a interface{} = "abc" // 在switch中使用 变量名.(type) 查询变量是由哪个类型数据赋值。 switch v := a.(type) { case string: fmt.Println("字符串") case int: fmt.Println("整型") default: fmt.Println("其他类型", v) } }
如果使用.(type)查詢類型的變數不是interface{}類型,則在編譯時會報如下錯誤:
cannot type switch on non-interface value a (type string)
如果在switch以外地方使用. (type),則在編譯時會提示如下錯誤:
use of .(type) outside type switch
所以,使用type進行類型查詢時,只能在switch中使用,且使用類型查詢的變數類型必須是interface{}
PHP中文網,有大量免費的Golang入門教學,歡迎大家學習!
以上是golang 型什麼意思的詳細內容。更多資訊請關注PHP中文網其他相關文章!