mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-26 06:46:27 +00:00
ch2-05 review
This commit is contained in:
parent
fa63a48058
commit
82ec0c025d
@ -1,23 +1,24 @@
|
|||||||
## 2.5. 類型聲明
|
## 2.5. 類型
|
||||||
|
|
||||||
變量或表達式的類型定義了對應存儲值的特徵, 例如數值的存儲大小(或者是元素的bit個數), 它們在內部是如何表達的, 是否支持一些操作符, 以及它們自己關聯的方法集,
|
變量或表達式的類型定義了對應存儲值的屬性特徵,例如數值在內存的存儲大小(或者是元素的bit個數),它們在內部是如何表達的,是否支持一些操作符,以及它們自己關聯的方法集等。
|
||||||
|
|
||||||
在任何程序中都會有一些變量有着相同的內部實現, 但是表示完全不同的概念.
|
在任何程序中都會存在一些變量有着相同的內部結構,但是卻表示完全不同的概念。例如,一個int類型的變量可以用來表示一個循環的迭代索引、或者一個時間戳、或者一個文件描述符、或者一個月份;一個float64類型的變量可以用來表示每秒移動幾米的速度、或者是不同溫度單位下的溫度;一個字符串可以用來表示一個密碼或者一個顔色的名稱。
|
||||||
例如, int 類型的變量可以用來表示一個循環的迭代索引, 或者一個時間戳, 或者一個文件描述符, 或者一個月份; 一個 float64 類型的變量可以用來表示每秒幾米的速度, 或者是不同溫度單位的溫度;
|
|
||||||
一個字符串可以用來表示一個密碼或者一個顔色的名稱.
|
|
||||||
|
|
||||||
一個類型的聲明創建了一個新的類型名稱, 和現有類型具有相同的底層結構.
|
一個類型聲明語句創建了一個新的類型名稱,和現有類型具有相同的底層結構。新命名的類型提供了一個方法,用來分隔不同概念的類型,這樣卽使它們底層類型相同也是不兼容的。
|
||||||
新命名的類型提供了一個方法, 用來分隔不同概念的類型, 卽使它們底層類型相同也是不兼容的.
|
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
type name underlying-type
|
type 類型名字 底層類型
|
||||||
```
|
```
|
||||||
|
|
||||||
類型的聲明一般齣現在包級别, 因此如果新創建的類型名字名字的首字符大寫, 則在外部包也可以使用.
|
類型聲明語句一般齣現在包一級,因此如果新創建的類型名字的首字符大寫,則在外部包也可以使用。
|
||||||
|
|
||||||
爲了説明類型聲明, 我們將不同溫度單位分别定義爲不同的類型:
|
譯註:對於中文漢字,Unicode標誌都作爲小寫字母處理,因此中文的命名默認不能導齣;不過國內的用戶針對該問題提齣了我們自己的間接,根據RobPike的迴複,在Go2中有可能會將中日韓等字符當作大寫字母處理。下面是RobPik在 [Issue763](https://github.com/golang/go/issues/5763) 的迴複:
|
||||||
|
|
||||||
爲了説明類型聲明,讓我們把不同溫度范圍分爲不同的類型:
|
> A solution that's been kicking around for a while:
|
||||||
|
>
|
||||||
|
> For Go 2 (can't do it before then): Change the definition to “lower 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.
|
||||||
|
|
||||||
|
爲了説明類型聲明,我們將不同溫度單位分别定義爲不同的類型:
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
gopl.io/ch2/tempconv0
|
gopl.io/ch2/tempconv0
|
||||||
@ -32,7 +33,7 @@ type Fahrenheit float64 // 華氏溫度
|
|||||||
const (
|
const (
|
||||||
AbsoluteZeroC Celsius = -273.15 // 絶對零度
|
AbsoluteZeroC Celsius = -273.15 // 絶對零度
|
||||||
FreezingC Celsius = 0 // 結冰點溫度
|
FreezingC Celsius = 0 // 結冰點溫度
|
||||||
BoilingC Celsius = 100 // 沸水問題
|
BoilingC Celsius = 100 // 沸水溫度
|
||||||
)
|
)
|
||||||
|
|
||||||
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
|
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
|
||||||
@ -40,16 +41,13 @@ func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
|
|||||||
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
|
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
|
||||||
```
|
```
|
||||||
|
|
||||||
這個包定義了兩種類型, Celsius 和 Fahrenheit 分别對應不同的溫度單位. 它們都有着相同的底層類型 float64, 但是它們是不同的數據類型, 因此它們不可以被相互比較或混在一個表達式計算. 可以區分類型, 可以避免一些像無意中結合單位的溫度進行計算的錯誤; 因爲需要一個類似 Celsius(t) 或 Fahrenheit(t) 顯式的轉型操作纔能將 float64 轉爲對應的類型. Celsius(t) 和 Fahrenheit(t) 是類型轉換操作, 併不是函數調用. 類型轉換不會改變值本身, 但是會使它們的語義發生變化. 另一方面, 函數 CToF 和 FToC 則是對兩個不同的溫度單位進行轉換, 它們會返迴不同的值.
|
我们在這個包声明了兩種類型:Celsius和Fahrenheit分别對應不同的溫度單位。它們虽然有着相同的底層類型float64,但是它們是不同的數據類型,因此它們不可以被相互比較或混在一個表達式运算。刻意區分類型,可以避免一些像無意中使用不同單位的溫度混合計算导致的錯誤;因此需要一個類似Celsius(t)或Fahrenheit(t)形式的顯式轉型操作纔能將float64轉爲對應的類型。Celsius(t)和Fahrenheit(t)是類型轉換操作,它们併不是函數調用。類型轉換不會改變值本身,但是會使它們的語義發生變化。另一方面,CToF和FToC两个函数則是對不同溫度單位下的温度进行换算,它們會返迴不同的值。
|
||||||
|
|
||||||
對於每一個類型 T, 都有一個對應的類型轉換操作 T(x), 用於將 x 轉爲 T 類型.
|
對於每一個類型T,都有一個對應的類型轉換操作T(x),用於將x轉爲T類型(译注:如果T是指针类型,可能会需要用小括弧包装T,比如`(*int)(0)`)。隻有當兩個類型的底層基礎類型相同時,才允許這種轉型操作,或者是兩者都是指向相同底層結構的指針類型,這些轉換隻改變類型而不會影響值本身。如果x是可以賦值給T類型的值,那麽x必然也可以被轉爲T類型,但是一般沒有这个必要。
|
||||||
隻有當兩個類型的底層基礎類型相同時, 纔允許這種轉型操作, 或者是兩者都是指向相同底層結構的指針類型,
|
|
||||||
這些轉換隻改變類型而不會影響值本身. 如果x是可以賦值給T類型的, 那麽x必然可以被轉爲T類型, 但是一般沒有必要.
|
|
||||||
|
|
||||||
數值類型之間的轉型也是允許的, 併且在字符串和一些特定切片之間也是可以轉換的, 在下一章我們會看到這樣的例子. 這類轉換可能改變值的表現. 例如, 將一個浮點數轉爲整數將丟棄小數部分, 將一個字符串轉爲 []byte 切片將拷貝一個字符串數據的副本. 在任何情況下, 運行時不會發送轉換失敗的錯誤(譯註: 錯誤隻會發生在編譯階段).
|
數值類型之間的轉型也是允許的,併且在字符串和一些特定类型的slice之間也是可以轉換的,在下一章我們會看到這樣的例子。這類轉換可能改變值的表現。例如,將一個浮點數轉爲整數將丟棄小數部分,將一個字符串轉爲`[]byte`类型的slice將拷貝一個字符串數據的副本。在任何情況下,運行時不會發生轉換失敗的錯誤(譯註: 錯誤隻會發生在編譯階段)。
|
||||||
|
|
||||||
底層數據類型決定了內部結構和表達方式, 也包決定是否可以像底層類型一樣對內置運算符的支持.
|
底層數據類型決定了內部結構和表達方式,也決定是否可以像底層類型一樣對內置運算符的支持。這意味着,Celsius和Fahrenheit類型的算術运算行爲和底層的float64類型是一樣的,正如我们所期望的那样。
|
||||||
這意味着, Celsius 和 Fahrenheit 類型的算術行爲和底層的 float64 類型一樣, 正如你所期望的.
|
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C
|
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C
|
||||||
@ -58,8 +56,7 @@ fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // "180" °F
|
|||||||
fmt.Printf("%g\n", boilingF-FreezingC) // compile error: type mismatch
|
fmt.Printf("%g\n", boilingF-FreezingC) // compile error: type mismatch
|
||||||
```
|
```
|
||||||
|
|
||||||
比較運算符 `==` 和 `<` 也可以用來比較一個命名類型的變量和另一個有相同類型的變量或相同的底層類型的值做比較.
|
比較運算符`==`和`<`也可以用來比較一個命名類型的變量和另一個有相同類型的變量或有相同底層類型的值做比較。但是如果兩個值有着不同的類型,則不能直接進行比較:
|
||||||
但是如果兩個值有着不同的類型, 則不能直接進行比較:
|
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
var c Celsius
|
var c Celsius
|
||||||
@ -70,19 +67,19 @@ fmt.Println(c == f) // compile error: type mismatch
|
|||||||
fmt.Println(c == Celsius(f)) // "true"!
|
fmt.Println(c == Celsius(f)) // "true"!
|
||||||
```
|
```
|
||||||
|
|
||||||
註意最後那個語句. 盡管看起來想函數調用, 但是Celsius(f)類型轉換, 併不會改變值, 它僅僅是改變值的類型而已. 測試爲眞的原因是因爲 c 和 g 都是零值.
|
註意最後那個語句。盡管看起來想函數調用,但是Celsius(f)是類型轉換操作,它併不會改變值,僅僅是改變值的類型而已。測試爲眞的原因是因爲c和g都是零值。
|
||||||
|
|
||||||
一個命名的類型可以提供符號方便, 特别是可以避免一遍又一遍地書寫複雜類型(譯註: 例如用匿名的結構體定義變量). 雖然對於像float64這種簡單的底層類型沒有簡潔很多, 但是如果是複雜的類型將會簡潔很多, 正如我們卽將討論的結構體類型:
|
一個命名的類型可以提供书写方便,特别是可以避免一遍又一遍地書寫複雜類型(譯註:例如用匿名的結構體定義變量)。雖然對於像float64這種簡單的底層類型沒有簡潔很多,但是如果是複雜的類型將會簡潔很多,特别是我們卽將討論的結構體類型。
|
||||||
|
|
||||||
命名類型還可以爲該類型的值定義新的行爲. 這些行爲表示爲一組關聯到類型的函數, 我們成爲類型的方法集. 我們將在第六章討論方法的細節, 這里值説寫簡單用法.
|
命名類型還可以爲該類型的值定義新的行为。這些行爲表示爲一組關聯到该類型的函數集合,我們称爲類型的方法集。我們將在第六章中討論方法的細節,這里值説寫簡單用法。
|
||||||
|
|
||||||
下面的聲明, Celsius 類型的參數 c 齣現在了函數名的前面, 表示聲明一個 Celsius 類型的 名叫 String 的方法, 方法返迴 帶着 °C 溫度單位 的參數 c 的數字打印字符串:
|
下面的聲明语句,Celsius類型的參數c齣現在了函數名的前面,表示聲明的是Celsius類型的一个叫名叫String的方法,该方法返迴该类型对象c帶着°C溫度單位的字符串:
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
|
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
|
||||||
```
|
```
|
||||||
|
|
||||||
許多類型都會定義個 String 方法, 因爲當然用 fmt 包的打印方法時, 將會優先使用 String 方法返迴的結果打印, 將在 7.1節 講述.
|
許多類型都會定義一個String方法,因爲當使用fmt包的打印方法時,將會優先使用该类型对应的String方法返迴的結果打印,我们將在7.1節講述。
|
||||||
|
|
||||||
```Go
|
```Go
|
||||||
c := FToC(212.0)
|
c := FToC(212.0)
|
||||||
|
Loading…
Reference in New Issue
Block a user