[英]Explain Type Assertions in Go
我正在閱讀Go Go Programming Language中的類型斷言x.(T)
並且不理解它們。
我知道有不同的場景:
這是我不明白的:
我也搜索了這個話題,仍然不明白。
在一行中:
x.(T)
斷言x
不是nil,而x
存儲的值是T
類型。
x
是否為零 t := x.(T)
=> t是T
型; 如果x
為零,則會發生恐慌。
t,ok := x.(T)
=>如果x
為n或者類型為T
=> ok
則為false
否則ok
為true
且t
為T
類型。
想象一下,你需要計算4種不同形狀的面積:圓形,方形,矩形和三角形。 您可以使用名為Area()
的新方法定義新類型,如下所示:
type Circle struct {
Radius float64
}
func (t Circle) Area() float64 {
return math.Pi * t.Radius * t.Radius
}
而對於Triangle
:
type Triangle struct {
A, B, C float64 // lengths of the sides of a triangle.
}
func (t Triangle) Area() float64 {
p := (t.A + t.B + t.C) / 2.0 // perimeter half
return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
}
對於Rectangle
:
type Rectangle struct {
A, B float64
}
func (t Rectangle) Area() float64 {
return t.A * t.B
}
對於Square
:
type Square struct {
A float64
}
func (t Square) Area() float64 {
return t.A * t.A
}
這里有Circle
,半徑為1.0,其他形狀有兩邊:
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
有趣! 我們如何在一個地方收集它們?
首先,您需要使用Shape interface
將它們全部收集在一片形狀中[]Shape
:
type Shape interface {
Area() float64
}
現在你可以像這樣收集它們:
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
畢竟, Circle
是一個Shape
而Triangle
也是一個Shape
。
現在,您可以使用單個語句v.Area()
打印每個形狀的區域:
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
}
所以Area()
是所有形狀之間的通用接口。 現在,我們如何計算和調用不常見的方法,如使用上述shapes
的三角形角度?
func (t Triangle) Angles() []float64 {
return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
}
func angle(a, b, c float64) float64 {
return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
}
現在是時候從上面的shapes
提取Triangle
了:
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
if t, ok := v.(Triangle); ok {
fmt.Println("Angles:", t.Angles())
}
}
使用t, ok := v.(Triangle)
我們請求類型斷言,這意味着我們要求編譯器嘗試將類型為Shape
v
轉換為類型Triangle
,這樣如果成功,則ok
將為true
否則為false
,然后如果它成功調用t.Angles()
來計算三角形的三個角度。
這是輸出:
Circle (Radius: 1) Area: 3.141592653589793
Square (Sides: 1.772453) Area: 3.1415896372090004
Rectangle (Sides: 5, 10) Area: 50
Triangle (Sides: 10, 4, 7) Area: 10.928746497197197
Angles: [128.68218745348943 18.194872338766785 33.12294020774379]
以及整個工作示例代碼:
package main
import "fmt"
import "math"
func main() {
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
if t, ok := v.(Triangle); ok {
fmt.Println("Angles:", t.Angles())
}
}
}
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
type Triangle struct {
A, B, C float64 // lengths of the sides of a triangle.
}
type Rectangle struct {
A, B float64
}
type Square struct {
A float64
}
func (t Circle) Area() float64 {
return math.Pi * t.Radius * t.Radius
}
// Heron's Formula for the area of a triangle
func (t Triangle) Area() float64 {
p := (t.A + t.B + t.C) / 2.0 // perimeter half
return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
}
func (t Rectangle) Area() float64 {
return t.A * t.B
}
func (t Square) Area() float64 {
return t.A * t.A
}
func (t Circle) String() string {
return fmt.Sprint("Circle (Radius: ", t.Radius, ")")
}
func (t Triangle) String() string {
return fmt.Sprint("Triangle (Sides: ", t.A, ", ", t.B, ", ", t.C, ")")
}
func (t Rectangle) String() string {
return fmt.Sprint("Rectangle (Sides: ", t.A, ", ", t.B, ")")
}
func (t Square) String() string {
return fmt.Sprint("Square (Sides: ", t.A, ")")
}
func (t Triangle) Angles() []float64 {
return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
}
func angle(a, b, c float64) float64 {
return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
}
另見:
對於接口類型的表達式x和類型T,主表達式
x.(T)
聲明x不是nil並且存儲在x中的值是T類型。符號x。(T)稱為類型斷言。
更確切地說,如果T不是接口類型,則x。(T)斷言x的動態類型與類型T相同。在這種情況下,T必須實現x的(接口)類型; 否則類型斷言無效,因為x不可能存儲類型T的值。如果T是接口類型,則x。(T)斷言x的動態類型實現接口T.
如果類型斷言成立,則表達式的值是存儲在x中的值,其類型為T. 如果類型斷言為false,則發生運行時混亂。 換句話說,即使動態類型的x僅在運行時是已知的,x。(T)的類型在正確的程序中已知為T.
var x interface{} = 7 // x has dynamic type int and value 7 i := x.(int) // i has type int and value 7 type I interface { m() } var y I s := y.(string) // illegal: string does not implement I (missing method m) r := y.(io.Reader) // r has type io.Reader and y must implement both I and io.Reader
在特殊形式的賦值或初始化中使用的類型斷言
v, ok = x.(T) v, ok := x.(T) var v, ok = x.(T)
產生一個額外的無類型布爾值。 如果斷言成立,則ok的值為true。 否則為false,v的值為類型T的零值。 在這種情況下,不會發生運行時混亂 。
問題 :當T是interface{}
而不是具體類型時,斷言x.(T)
返回什么?
答案 :
它聲明x不是nil,並且存儲在x中的值是T類型。
例如這種恐慌(編譯:成功,運行: panic: interface conversion: interface is nil, not interface {}
):
package main
func main() {
var i interface{} // nil
var _ = i.(interface{})
}
這工作(運行:確定):
package main
import "fmt"
func main() {
var i interface{} // nil
b, ok := i.(interface{})
fmt.Println(b, ok) // <nil> false
i = 2
c, ok := i.(interface{})
fmt.Println(c, ok) // 2 true
//var j int = c // cannot use c (type interface {}) as type int in assignment: need type assertion
//fmt.Println(j)
}
輸出:
<nil> false
2 true
注意:此處c
的類型為interface {}
而不是int
。
使用注釋輸出查看此工作示例代碼:
package main
import "fmt"
func main() {
const fm = "'%T'\t'%#[1]v'\t'%[1]v'\t%v\n"
var i interface{}
b, ok := i.(interface{})
fmt.Printf(fm, b, ok) // '<nil>' '<nil>' '<nil>' false
i = 2
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'int' '2' '2' true
i = "Hi"
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'string' '"Hi"' 'Hi' true
i = new(interface{})
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // '*interface {}' '(*interface {})(0xc042004330)' '0xc042004330' true
i = struct{}{}
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'struct {}' 'struct {}{}' '{}' true
i = fmt.Println
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'func(...interface {}) (int, error)' '(func(...interface {}) (int, error))(0x456740)' '0x456740' true
i = Shape.Area
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'func(main.Shape) float64' '(func(main.Shape) float64)(0x401910)' '0x401910' true
}
type Shape interface {
Area() float64
}
常用用例:檢查返回的錯誤是否為T類型。
https://golang.org/ref/spec#Type_assertions
對於單個返回值斷言:當它失敗時程序會發生混亂。
對於兩個返回值斷言:當它失敗時,第二個參數設置為false並且程序不會發生混亂。
類型斷言是x。(T)表示法,其中x是接口類型,T是類型。 另外,存儲在x中的實際值是T類型,T必須滿足x的接口類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.