gopl-zh.github.com/ch7/ch7-10.md

58 lines
3.3 KiB
Markdown
Raw Normal View History

2015-12-09 07:45:11 +00:00
## 7.10. 類型斷言
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
類型斷言是一個使用在接口值上的操作。語法上它看起來像x.(T)被稱爲斷言類型這里x表示一個接口的類型和T表示一個類型。一個類型斷言檢査它操作對象的動態類型是否和斷言的類型匹配。
2015-12-09 07:45:11 +00:00
2016-01-18 02:36:12 +00:00
這里有兩種可能。第一種如果斷言的類型T是一個具體類型然後類型斷言檢査x的動態類型是否和T相同。如果這個檢査成功了類型斷言的結果是x的動態值當然它的類型是T。換句話説具體類型的類型斷言從它的操作對象中獲得具體的值。如果檢査失敗接下來這個操作會拋出panic。例如
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
```go
var w io.Writer
w = os.Stdout
f := w.(*os.File) // success: f == os.Stdout
c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer
```
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
第二種如果相反斷言的類型T是一個接口類型然後類型斷言檢査是否x的動態類型滿足T。如果這個檢査成功了動態值沒有獲取到這個結果仍然是一個有相同類型和值部分的接口值但是結果有類型T。換句話説對一個接口類型的類型斷言改變了類型的表述方式改變了可以獲取的方法集合通常更大但是它保護了接口值內部的動態類型和值的部分。
在下面的第一個類型斷言後w和rw都持有os.Stdout因此它們每個有一個動態類型*os.File但是變量w是一個io.Writer類型隻對外公開出文件的Write方法然而rw變量也隻公開它的Read方法。
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
```go
var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
w = new(ByteCounter)
rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method
```
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
如果斷言操作的對象是一個nil接口值那麽不論被斷言的類型是什麽這個類型斷言都會失敗。我們幾乎不需要對一個更少限製性的接口類型更少的方法集合做斷言因爲它表現的就像賦值操作一樣除了對於nil接口值的情況。
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
```go
w = rw // io.ReadWriter is assignable to io.Writer
w = rw.(io.Writer) // fails only if rw == nil
```
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
經常地我們對一個接口值的動態類型是不確定的併且我們更願意去檢驗它是否是一些特定的類型。如果類型斷言出現在一個預期有兩個結果的賦值操作中例如如下的定義這個操作不會在失敗的時候發生panic但是代替地返迴一個額外的第二個結果這個結果是一個標識成功的布爾值
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
```go
var w io.Writer = os.Stdout
f, ok := w.(*os.File) // success: ok, f == os.Stdout
b, ok := w.(*bytes.Buffer) // failure: !ok, b == nil
```
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
第二個結果常規地賦值給一個命名爲ok的變量。如果這個操作失敗了那麽ok就是false值第一個結果等於被斷言類型的零值在這個例子中就是一個nil的*bytes.Buffer類型。
這個ok結果經常立卽用於決定程序下面做什麽。if語句的擴展格式讓這個變的很簡潔
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
```go
if f, ok := w.(*os.File); ok {
2016-01-18 02:52:21 +00:00
// ...use f...
2016-01-18 02:36:12 +00:00
}
```
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
當類型斷言的操作對象是一個變量,你有時會看見原來的變量名重用而不是聲明一個新的本地變量,這個重用的變量會覆蓋原來的值,如下面這樣:
2016-01-18 02:52:21 +00:00
2016-01-18 02:36:12 +00:00
```go
if w, ok := w.(*os.File); ok {
2016-01-18 02:52:21 +00:00
// ...use w...
2016-01-18 02:36:12 +00:00
}
```