W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
原文鏈接:https://gopl-zh.github.io/ch2/ch2-05.html
變量或表達(dá)式的類型定義了對應(yīng)存儲(chǔ)值的屬性特征,例如數(shù)值在內(nèi)存的存儲(chǔ)大?。ɑ蛘呤窃氐腷it個(gè)數(shù)),它們在內(nèi)部是如何表達(dá)的,是否支持一些操作符,以及它們自己關(guān)聯(lián)的方法集等。
在任何程序中都會(huì)存在一些變量有著相同的內(nèi)部結(jié)構(gòu),但是卻表示完全不同的概念。例如,一個(gè)int類型的變量可以用來表示一個(gè)循環(huán)的迭代索引、或者一個(gè)時(shí)間戳、或者一個(gè)文件描述符、或者一個(gè)月份;一個(gè)float64類型的變量可以用來表示每秒移動(dòng)幾米的速度、或者是不同溫度單位下的溫度;一個(gè)字符串可以用來表示一個(gè)密碼或者一個(gè)顏色的名稱。
一個(gè)類型聲明語句創(chuàng)建了一個(gè)新的類型名稱,和現(xiàn)有類型具有相同的底層結(jié)構(gòu)。新命名的類型提供了一個(gè)方法,用來分隔不同概念的類型,這樣即使它們底層類型相同也是不兼容的。
type 類型名字 底層類型
類型聲明語句一般出現(xiàn)在包一級,因此如果新創(chuàng)建的類型名字的首字符大寫,則在包外部也可以使用。
譯注:對于中文漢字,Unicode標(biāo)志都作為小寫字母處理,因此中文的命名默認(rèn)不能導(dǎo)出;不過國內(nèi)的用戶針對該問題提出了不同的看法,根據(jù)RobPike的回復(fù),在Go2中有可能會(huì)將中日韓等字符當(dāng)作大寫字母處理。下面是RobPik在 Issue763 的回復(fù):
A solution that's been kicking around for a while:
For Go 2 (can't do it before then): Change the definition to “l(fā)ower case letters and _ are package-local; all else is exported”. Then with non-cased languages, such as Japanese, we can write 日本語 for an exported name and _日本語 for a local name. This rule has no effect, relative to the Go 1 rule, with cased languages. They behave exactly the same.
為了說明類型聲明,我們將不同溫度單位分別定義為不同的類型:
gopl.io/ch2/tempconv0
// Package tempconv performs Celsius and Fahrenheit temperature computations.
package tempconv
import "fmt"
type Celsius float64 // 攝氏溫度
type Fahrenheit float64 // 華氏溫度
const (
AbsoluteZeroC Celsius = -273.15 // 絕對零度
FreezingC Celsius = 0 // 結(jié)冰點(diǎn)溫度
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) }
我們在這個(gè)包聲明了兩種類型:Celsius和Fahrenheit分別對應(yīng)不同的溫度單位。它們雖然有著相同的底層類型float64,但是它們是不同的數(shù)據(jù)類型,因此它們不可以被相互比較或混在一個(gè)表達(dá)式運(yùn)算??桃鈪^(qū)分類型,可以避免一些像無意中使用不同單位的溫度混合計(jì)算導(dǎo)致的錯(cuò)誤;因此需要一個(gè)類似Celsius(t)或Fahrenheit(t)形式的顯式轉(zhuǎn)型操作才能將float64轉(zhuǎn)為對應(yīng)的類型。Celsius(t)和Fahrenheit(t)是類型轉(zhuǎn)換操作,它們并不是函數(shù)調(diào)用。類型轉(zhuǎn)換不會(huì)改變值本身,但是會(huì)使它們的語義發(fā)生變化。另一方面,CToF和FToC兩個(gè)函數(shù)則是對不同溫度單位下的溫度進(jìn)行換算,它們會(huì)返回不同的值。
對于每一個(gè)類型T,都有一個(gè)對應(yīng)的類型轉(zhuǎn)換操作T(x),用于將x轉(zhuǎn)為T類型(譯注:如果T是指針類型,可能會(huì)需要用小括弧包裝T,比如(*int)(0)
)。只有當(dāng)兩個(gè)類型的底層基礎(chǔ)類型相同時(shí),才允許這種轉(zhuǎn)型操作,或者是兩者都是指向相同底層結(jié)構(gòu)的指針類型,這些轉(zhuǎn)換只改變類型而不會(huì)影響值本身。如果x是可以賦值給T類型的值,那么x必然也可以被轉(zhuǎn)為T類型,但是一般沒有這個(gè)必要。
數(shù)值類型之間的轉(zhuǎn)型也是允許的,并且在字符串和一些特定類型的slice之間也是可以轉(zhuǎn)換的,在下一章我們會(huì)看到這樣的例子。這類轉(zhuǎn)換可能改變值的表現(xiàn)。例如,將一個(gè)浮點(diǎn)數(shù)轉(zhuǎn)為整數(shù)將丟棄小數(shù)部分,將一個(gè)字符串轉(zhuǎn)為[]byte
類型的slice將拷貝一個(gè)字符串?dāng)?shù)據(jù)的副本。在任何情況下,運(yùn)行時(shí)不會(huì)發(fā)生轉(zhuǎn)換失敗的錯(cuò)誤(譯注: 錯(cuò)誤只會(huì)發(fā)生在編譯階段)。
底層數(shù)據(jù)類型決定了內(nèi)部結(jié)構(gòu)和表達(dá)方式,也決定是否可以像底層類型一樣對內(nèi)置運(yùn)算符的支持。這意味著,Celsius和Fahrenheit類型的算術(shù)運(yùn)算行為和底層的float64類型是一樣的,正如我們所期望的那樣。
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C
boilingF := CToF(BoilingC)
fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // "180" °F
fmt.Printf("%g\n", boilingF-FreezingC) // compile error: type mismatch
比較運(yùn)算符==
和<
也可以用來比較一個(gè)命名類型的變量和另一個(gè)有相同類型的變量,或有著相同底層類型的未命名類型的值之間做比較。但是如果兩個(gè)值有著不同的類型,則不能直接進(jìn)行比較:
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"!
注意最后那個(gè)語句。盡管看起來像函數(shù)調(diào)用,但是Celsius(f)是類型轉(zhuǎn)換操作,它并不會(huì)改變值,僅僅是改變值的類型而已。測試為真的原因是因?yàn)閏和f都是零值。
一個(gè)命名的類型可以提供書寫方便,特別是可以避免一遍又一遍地書寫復(fù)雜類型(譯注:例如用匿名的結(jié)構(gòu)體定義變量)。雖然對于像float64這種簡單的底層類型沒有簡潔很多,但是如果是復(fù)雜的類型將會(huì)簡潔很多,特別是我們即將討論的結(jié)構(gòu)體類型。
命名類型還可以為該類型的值定義新的行為。這些行為表示為一組關(guān)聯(lián)到該類型的函數(shù)集合,我們稱為類型的方法集。我們將在第六章中討論方法的細(xì)節(jié),這里只說些簡單用法。
下面的聲明語句,Celsius類型的參數(shù)c出現(xiàn)在了函數(shù)名的前面,表示聲明的是Celsius類型的一個(gè)名叫String的方法,該方法返回該類型對象c帶著°C溫度單位的字符串:
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
許多類型都會(huì)定義一個(gè)String方法,因?yàn)楫?dāng)使用fmt包的打印方法時(shí),將會(huì)優(yōu)先使用該類型對應(yīng)的String方法返回的結(jié)果打印,我們將在7.1節(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
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: