解释 Go 中的类型断言 [英] Explain Type Assertions in Go
问题描述
我正在阅读 x.(T)" rel="nofollow noreferrer" title="Go 编程语言">Go 编程语言 并且不了解它们.
I'm reading about type assertions x.(T)
in The Go Programming Language and don't understand them.
我知道有不同的情况:
- T 是具体类型或接口
- 可以返回一个(断言值?)或两个(确定)值
这是我不明白的:
- 我为什么要使用它们?
- 他们究竟返回了什么?
我也用谷歌搜索过这个话题,但仍然不明白.
I have also googled on the topic and still don't understand.
推荐答案
简短回答
一行:
x.(T)
断言 x
不是 nil 并且存储在 x
中的值是 T代码>.
x.(T)
asserts thatx
is not nil and that the value stored inx
is of typeT
.
我为什么要使用它们:
- 检查
x
是否为零 - 检查它是否可以转换(断言)为另一种类型
- 转换(断言)为另一种类型
t := x.(T)
=> t 是T
类型;如果x
为 nil,它会崩溃.t := x.(T)
=> t is of typeT
; ifx
is nil, it panics.
t,ok := x.(T)
=> 如果 x
为 nil 或不是 T
类型 =>ok
是 false
否则 ok
是 true
并且 t
是 类型T
.
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
.
假设您需要计算 4 种不同形状的面积:圆形、方形、矩形和三角形.您可以使用名为 Area()
的新方法定义新类型,如下所示:
Imagine you need to calculate area of 4 different shapes: Circle, Square, Rectangle and Triangle. You may define new types with a new method called Area()
, like this:
type Circle struct {
Radius float64
}
func (t Circle) Area() float64 {
return math.Pi * t.Radius * t.Radius
}
对于三角形
:
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))
}
对于矩形
:
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,其他形状有边:
Here you have Circle
, with radius of 1.0, and other shapes with their sides:
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
有趣!我们如何将它们全部收集到一个地方?
首先,您需要 Shape interface
将它们全部收集到一个形状中 []Shape
:
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
:
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},
}
毕竟,Circle
是一个 Shape
而 Triangle
也是一个 Shape
.
现在您可以使用单个语句 v.Area()
打印每个形状的面积:
After all, Circle
is a Shape
and Triangle
is a Shape
too.
Now you can print the area of each shape using the single statement v.Area()
:
for _, v := range shapes {
fmt.Println(v, " Area:", v.Area())
}
所以 Area()
是所有形状之间的通用接口.现在,我们如何使用上面的shapes
来计算和调用不常见的方法,比如三角形的角度?
So Area()
is a common interface between all shapes.
Now, how can we calculate and call uncommon method like angles of triangle using above 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
:
Now it's time to extract Triangle
from above shapes
:
for _, v := range shapes {
fmt.Println(v, " Area:", v.Area())
if t, ok := v.(Triangle); ok {
fmt.Println("Angles:", t.Angles())
}
}
使用 t, ok := v.(Triangle)
我们请求类型断言,这意味着我们要求编译器尝试转换 Shape<类型的
v
/code> 输入 Triangle
,这样如果成功,ok
将是 true
否则 false
,和然后如果成功调用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.
这是输出:
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, " Area:", 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,主要表达
For an expression x of interface type and a type T, the primary expression
x.(T)
断言 x 不是 nil 并且存储在 x 中的值是 T 类型.符号 x.(T) 称为类型断言.
更准确地说,如果 T 不是接口类型,则 x.(T) 断言x 的动态类型与类型 T 相同.在这种情况下,T 必须实现 x 的(接口)类型;否则类型断言是无效,因为 x 不可能存储类型 T 的值.如果T 是接口类型,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; 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. 如果类型断言为假,则一个发生运行时恐慌. 换句话说,即使动态类型的x 仅在运行时已知,x.(T) 的类型在 a正确的程序.
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. 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.
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)
产生一个额外的无类型布尔值.ok 的值为真,如果断言成立.否则它是假的,v 的值是类型 T 的零值.在这种情况下不会发生运行时恐慌.
yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.
<小时>
编辑
问题:当 T 是 interface{}
而不是具体类型时,断言 x.(T)
返回什么?
答案:
EDIT
Question: What does the assertion x.(T)
return when T is an interface{}
and not a concrete type?
Answer:
它断言 x 不是 nil 并且存储在 x 中的值是 T 类型.
It asserts that x is not nil and that the value stored in x is of type T.
例如这个恐慌(编译:成功,运行:panic:接口转换:接口为零,不是接口{}
):
E.g. this panics (compile: Success, Run: panic: interface conversion: interface is nil, not interface {}
):
package main
func main() {
var i interface{} // nil
var _ = i.(interface{})
}
这有效(运行:OK):
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)
}
输出:
<nil> false
2 true
注意:这里的 c
是 interface {}
而不是 int
类型.
NOTE: here c
is of type interface {}
and not int
.
请参阅此带有注释输出的工作示例代码:
See this working sample code with commented outputs:
package main
import "fmt"
func main() {
const fm = "'%T' '%#[1]v' '%[1]v' %v
"
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
}
这篇关于解释 Go 中的类型断言的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!