[英]Explain Type Assertions in Go
I'm reading about type assertions x.(T)
in The Go Programming Language and don't understand them. 我正在阅读Go Go Programming Language中的类型断言x.(T)
并且不理解它们。
I understand that there are different scenarios: 我知道有不同的场景:
This is what I don't understand: 这是我不明白的:
I have also googled on the topic and still don't understand. 我也搜索了这个话题,仍然不明白。
In one line: 在一行中:
x.(T)
asserts thatx
is not nil and that the value stored inx
is of typeT
.x.(T)
断言x
不是nil,而x
存储的值是T
类型。
x
is nil 检查x
是否为零 t := x.(T)
=> t is of type T
; t := x.(T)
=> t是T
型; if x
is nil, it panics. 如果x
为零,则会发生恐慌。
t,ok := x.(T)
=> if x
is nil or not of type T
=> ok
is false
otherwise ok
is true
and t
is of type T
. t,ok := x.(T)
=>如果x
为n或者类型为T
=> ok
则为false
否则ok
为true
且t
为T
类型。
Imagine you need to calculate area of 4 different shapes: Circle, Square, Rectangle and Triangle. 想象一下,你需要计算4种不同形状的面积:圆形,方形,矩形和三角形。 You may define new types with a new method called Area()
, like this: 您可以使用名为Area()
的新方法定义新类型,如下所示:
type Circle struct {
Radius float64
}
func (t Circle) Area() float64 {
return math.Pi * t.Radius * t.Radius
}
And for 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))
}
And for Rectangle
: 对于Rectangle
:
type Rectangle struct {
A, B float64
}
func (t Rectangle) Area() float64 {
return t.A * t.B
}
And for Square
: 对于Square
:
type Square struct {
A float64
}
func (t Square) Area() float64 {
return t.A * t.A
}
Here you have Circle
, with radius of 1.0, and other shapes with their sides: 这里有Circle
,半径为1.0,其他形状有两边:
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
Interesting! 有趣! How can we collect them all in one place? 我们如何在一个地方收集它们?
First you need Shape interface
to collect them all in one slice of shape []Shape
: 首先,您需要使用Shape interface
将它们全部收集在一片形状中[]Shape
:
type Shape interface {
Area() float64
}
Now you can collect them like this: 现在你可以像这样收集它们:
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
After all, Circle
is a Shape
and Triangle
is a Shape
too. 毕竟, Circle
是一个Shape
而Triangle
也是一个Shape
。
Now you can print the area of each shape using the single statement v.Area()
: 现在,您可以使用单个语句v.Area()
打印每个形状的区域:
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
}
So Area()
is a common interface between all shapes. 所以Area()
是所有形状之间的通用接口。 Now, how can we calculate and call uncommon method like angles of triangle using above 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
}
Now it's time to extract Triangle
from above shapes
: 现在是时候从上面的shapes
提取Triangle
了:
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
if t, ok := v.(Triangle); ok {
fmt.Println("Angles:", t.Angles())
}
}
Using t, ok := v.(Triangle)
we requested type assertions, meaning we asked the compiler to try to convert v
of type Shape
to type Triangle
, so that if it's successful, the ok
will be true
otherwise false
, and then if it is successful call t.Angles()
to calculate the triangle's three angles. 使用t, ok := v.(Triangle)
我们请求类型断言,这意味着我们要求编译器尝试将类型为Shape
v
转换为类型Triangle
,这样如果成功,则ok
将为true
否则为false
,然后如果它成功调用t.Angles()
来计算三角形的三个角度。
This is the output: 这是输出:
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]
And the whole working sample code: 以及整个工作示例代码:
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
}
Also see: 另见:
For an expression x of interface type and a type T, the primary expression 对于接口类型的表达式x和类型T,主表达式
x.(T)
asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion. 声明x不是nil并且存储在x中的值是T类型。符号x。(T)称为类型断言。
More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; 更确切地说,如果T不是接口类型,则x。(T)断言x的动态类型与类型T相同。在这种情况下,T必须实现x的(接口)类型; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T. 否则类型断言无效,因为x不可能存储类型T的值。如果T是接口类型,则x。(T)断言x的动态类型实现接口T.
If the type assertion holds, the value of the expression is the value stored in x and its type is T. If the type assertion is false, a run-time panic occurs. 如果类型断言成立,则表达式的值是存储在x中的值,其类型为T. 如果类型断言为false,则发生运行时混乱。 In other words, even though the dynamic type of x is known only at run time, the type of x.(T) is known to be T in a correct program. 换句话说,即使动态类型的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
A type assertion used in an assignment or initialization of the special form 在特殊形式的赋值或初始化中使用的类型断言
v, ok = x.(T) v, ok := x.(T) var v, ok = x.(T)
yields an additional untyped boolean value. 产生一个额外的无类型布尔值。 The value of ok is true if the assertion holds. 如果断言成立,则ok的值为true。 Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case . 否则为false,v的值为类型T的零值。 在这种情况下,不会发生运行时混乱 。
Question : What does the assertion x.(T)
return when T is an interface{}
and not a concrete type? 问题 :当T是interface{}
而不是具体类型时,断言x.(T)
返回什么?
Answer : 答案 :
It asserts that x is not nil and that the value stored in x is of type T. 它声明x不是nil,并且存储在x中的值是T类型。
Eg this panics (compile: Success, Run: 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{})
}
And this works (Run: OK): 这工作(运行:确定):
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)
}
Output: 输出:
<nil> false
2 true
NOTE: here c
is of type interface {}
and not int
. 注意:此处c
的类型为interface {}
而不是int
。
See this working sample code with commented outputs: 使用注释输出查看此工作示例代码:
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
}
Common usecase: check if returned error is of a type T. 常用用例:检查返回的错误是否为T类型。
https://golang.org/ref/spec#Type_assertions https://golang.org/ref/spec#Type_assertions
For a single return value assertion: when it fails the program panics. 对于单个返回值断言:当它失败时程序会发生混乱。
For a two return values assertion: when it fails second argument is set to false and the program doesn't panic. 对于两个返回值断言:当它失败时,第二个参数设置为false并且程序不会发生混乱。
A type assertion is the x.(T) notation where x is of interface type and T is a type. 类型断言是x。(T)表示法,其中x是接口类型,T是类型。 Additionally, the actual value stored in x is of type T, and T must satisfy the interface type of x. 另外,存储在x中的实际值是T类型,T必须满足x的接口类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.