我正在阅读《 Go编程语言》x.(T)中的类型断言,但不理解它们。
x.(T)
我了解有不同的方案:
这是我不明白的:
我也用谷歌搜索这个话题,但仍然不明白。
一行:
x.(T)断言这x不是nil并且存储的值x是type T。
x
T
t := x.(T)=> t是类型T;如果x为零,则表示恐慌。
t := x.(T)
t,ok := x.(T)=>如果x是零或不类型T=> ok是false否则ok是true与t是类型的T。
t,ok := x.(T)
ok
false
true
t
假设您需要计算4种不同形状的面积:圆形,正方形,矩形和三角形。您可以使用称为的新方法来定义新类型Area(),如下所示:
Area()
type Circle struct { Radius float64 } func (t Circle) Area() float64 { return math.Pi * t.Radius * t.Radius }
对于Triangle:
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:
Rectangle
type Rectangle struct { A, B float64 } func (t Rectangle) Area() float64 { return t.A * t.B }
对于Square:
Square
type Square struct { A float64 } func (t Square) Area() float64 { return t.A * t.A }
在这里,您有Circle,其半径为1.0,其侧面具有其他形状:
Circle
shapes := []Shape{ Circle{1.0}, Square{1.772453}, Rectangle{5, 10}, Triangle{10, 4, 7}, }
有趣!我们如何将它们全部收集在一个地方? 首先,您需要Shape interface将它们全部收集成一片形状[]Shape:
Shape interface
[]Shape
type Shape interface { Area() float64 }
现在,您可以像这样收集它们:
毕竟,Circle是Shape和Triangle是Shape太。 现在,您可以使用单个语句打印每个形状的区域v.Area():
Shape
v.Area()
for _, v := range shapes { fmt.Println(v, "\tArea:", v.Area()) }
Area()所有形状之间的通用接口也是如此。现在,我们如何使用上述方法来计算和调用像三角形的角之类的不常见方法shapes?
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 }
现在是时候Triangle从上方提取shapes:
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)我们要求的类型断言,这意味着我们要求编译器尝试将vtype 转换Shape为type Triangle,以便如果成功,ok则将为true否则false,然后如果成功,则调用t.Angles()计算三角形的三个角度。
t, ok := v.(Triangle)
v
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 }
另请参阅:
类型断言
对于具有接口类型和类型T的表达式x,主要表达式 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的表达式x,主要表达式
断言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类型的零值。 在这种情况下,不会发生运行时恐慌 。
问题 :x.(T)当T是an interface{}而不是具体类型时,断言返回什么? 答 :
interface{}
它断言x不是nil,并且x中存储的值是T类型。
例如,此恐慌(编译:成功,运行:)panic: interface conversion: interface is nil, not interface {}:
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。
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 }