在Go中解释类型断言 [英] Explain Type Assertions in Go
问题描述
我正在阅读关于类型断言 x。(T)
in Go编程语言并不理解它们。
据我所知,场景:
- T是一个具体类型或接口
- 一个(声明值?)或可以返回两个(ok)值
这是我不明白的:
我也在这个主题上搜索了一下,但仍然不明白。
在一行中:
x。(T)
断言x
不为零,而存储在x
类型为T
。
我会使用它们:
- 检查
x
是否为零 - 检查它是否可以转换(assert)为另一种类型
- 转换(assert)为另一种类型
他们返回的是什么:
-
t:=如果
=> t的类型为x
为零,x。(T)T
它恐慌。 -
t,ok:= x。(T)
=> ifx
是零或者不是T =>
ok
是false
否则ok
是true
和t 类型为
T
。
< hr>
想象一下,您需要计算4个不同s的面积hapes:圆形,正方形,矩形和三角形。
所以你可以使用新方法定义新类型,如Area()
,如下所示:
类型Circle结构{
半径float64
}
func(t Circle)Area()float64 {
return math .Pi * t.Radius * t.Radius
}
并且对于
Triangle
:type Triangle struct {
A,B,C float64 //三角形边的长度。
}
func(t三角形)面积()float64 {
p:=(tA + tB + tC)/ 2.0 //周长一半
返回math.Sqrt(p * p - tA)*(p - tB)*(p - tC))
}
对于
Rectangle
:type Rectangle struct {
A,B float64
}
func(t Rectangle)Area()float64 {
return tA * tB
}
对于
Square
:类型Square结构{
A float64
}
func(t Square)Area )float64 {
return tA * tA
}
这里
Circle
,其半径为1.0,其他形状为它们的边:
shapes:= [] Shape {
Circle {1.0},
Square {1.772453},
Rectangle {5,10},
Triangle {10,4, 7},
}
有趣!如何将它们全部收集在一个地方?
首先你需要Shape接口
将它们全部收集在一个Slice of Shape中[ ] Shape :
类型Shape接口{
区域()float64
}
现在您可以像这样收集它们:
$ b $ $ {$}},
Square {1.772453}shapes:= [ ,
Rectangle {5,10},
Triangle {10,4,7},
}
毕竟
Circle
是一个形状
和三角形
也是形状
。
现在您可以使用单个语句v打印每个形状的区域。 Area()
:
$ bfor _,v:=范围形状{
fmt.Println(v,\ tArea:,v.Area())
}
因此
Area()
是常见的i所有形状之间的接口。
现在如何使用上面的shapes
来计算和调用像三角形之类的非常规方法:func(t Triangle)Angles()float64 {
return [] float64 {angle(tB,tC,tA),angle(tA,tC, (a * b + b * bc),角度(tA,tB,tC)}
} * c)/(2 * a * b))* 180.0 / math.Pi
}
<现在是时候从形状
中提取三角形
:
for _,v:=范围形状{
fmt.Println(v,\ tArea:,v.Area() )
如果t,ok:= v。(三角形); OK {
fmt.Println(Angles:,t.Angles())
}
}
使用
t,ok:= v。(三角形)
我们请求了类型断言。
表示我们从编译器询问尝试将Shape
类型的v
转换为类型三角
,所以如果成功,ok
将会是true
否则false
,那么如果成功调用t.Angles()
来计算三角形的三个角度。
这是输出:
$ p $Circle(Radius:1)Area:3.141592653589793
Square面积:10.88746497197197
角度:[128.68218745348943 18.194872338766785 33.12294020774379]面积:3.1415896372090004
方形(面积:5,10)面积:50
面积:
以及整个工作示例代码:
package main
importfmt
importmath
func main() {
shapes:= [] Shape {
Circle {1.0},
Square {1.772453},
Rectangle {5,10},
Triangl e {10,4,7},
}
for _,v:=范围形状{
fmt.Println(v,\ tArea:,v.Area())
if t,ok:= v。(Triangle); OK {
fmt.Println(Angles:,t.Angles())
}
}
}
类型Shape接口{
Area()float64
}
类型Circle结构{
半径float64
}
类型三角形结构{
A,B,C float64 //三角形边的长度。
}
类型矩形struct {
A,B float64
}
类型Square结构{
A float64
}
func(t Circle)Area()float64 {
return math.Pi * t.Radius * t.Radius
}
// Heron公式一个三角形
func(t三角形)面积()float64 {
p:=(tA + tB + tC)/ 2.0 //周长一半
返回数学sqrt(p *(p - tA )*(p - tB)*(p - tC))
}
func(t Rectangle)Area()float64 {
return tA * tB
}
func(t Square)Area()float64 {
return tA * tA
}
func(t Circle)String()string {
return fmt.Sprint(Circle(Radius:,t.Radius,))
}
func(t Triangle)String()string {
return fmt.Sprint(Triangle Sides:,tA,,,tB,,,tC,))
}
func(t Rectangle)String()string {
return fmt.Sprint 矩形(Sides:,tA,,,tB,))
}
func(t Square)String()string {
返回fmt.Sprint(Square(Sides:,tA,))
}
func(t三角形)Angles()[] float64 {
return [] float64 {角度(tB,tC,tA),角度(tA,tC,tB),角度(tA,tB,tC)}
}
函数角度(a,b,c float64)
return math.Acos((a * a + b * bc * c)/(2 * a * b))* 180.0 / math.Pi
}
另见:
$ b
对于接口类型和类型的表达式x T,主
表达式
x。(T)
断言x不为零,并且存储在x中的值为类型T.记号x。(T)称为类型断言。更精确地说,如果T不是接口类型,x。(T)断言x的
动态类型与类型T.在这种情况下,T必须
实现(接口) x的类型;否则类型断言是
无效,因为x不能存储类型T的值。如果
T是一个接口类型,x。(T)断言动态类型x
实现了接口T。
如果类型断言成立,则表达式的值是存储在x中的值
,其类型为T. 如果类型断言为false,则发生
运行时恐慌。换句话说,即使
x的动态类型只在运行时才知道,即x的类型。 (T)在一个
正确的程序中是已知的。var x interface {} = 7 // x有动态类型int和值7
i:= x。(int)//我有类型int和值7
类型I接口{m()}
var y I
s:= y。(string)//非法:字符串不实现I(缺少方法m)
r:= y。(io.Reader)// r具有类型io.Reader和y must实现I和io.Reader
类型asse rtion用于赋值或
特殊格式的初始化中
$ $ $ $ $ $ $ $ $ $ $ $ bv,ok:= x。(T)
var v,ok = x。(T)
会产生一个额外的无类型布尔值。如果
声明成立,则ok的值为true。否则它是假的,v的值是类型T的
零值。在这种情况下不会发生运行时恐慌。
编辑:
问题:当T是一个 $ b时,断言x。(T)
code> interface {} 而不是具体类型?
答案:
声明x不为零,并且存储在x中的值为T类型。
例如这个恐慌(编译:成功,运行:
panic:interface conversion:interface is nil,not interface {}
):
< pre class =lang-golang prettyprint-override>package main
func main(){
var i interface {} // nil
var _ = i。(interface {})
}
):
package main
importfmt
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 //在赋值中不能使用c(type interface {})作为int类型:need type assertion
//fmt.Println(j)
}
输出:
< ;零> false
2 true
注意:
c
类型为界面{}
而不是int
。
请看带有注释输出的示例代码:
package main
$ b importfmt
func main(){
const fm =''%T'\\'t'%#[1] v '\t'%[1] v'\t%v\\\
var i interface {}
b,ok:= i。(interface {})
fmt。 printf(fm,b,ok)//'< nil>''< nil>''< nil>'false
i = 2 $ b $,ok = i。 {})
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 )//结构{}结构{} {}'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。形状)float64''(func(main.Shape)float64)(0x401910)''0x401910'true
}
类型Shape接口{
Area()float64
}
I'm reading about type assertions
x.(T)
in The Go Programming Language and don't understand them.I understand that there are different scenarios:
- T is a concrete type or an interface
- One (asserted value?) or two (ok) values can be returned
This is what I don't understand:
- Why would I use them?
- What exactly do they return?
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
.Why would I use them:
- to check
x
is nil or not - to check is it convertible (assert) to another type
- convert (assert ) to another type
What exactly do they return:
t := x.(T)
=> t is of typeT
, ifx
is nil it panics.t,ok := x.(T)
=> ifx
is nil or not of typeT
=>ok
isfalse
otherwiseok
istrue
andt
is of typeT
.
Imagine you need to calculate Area of 4 different shapes: Circle, Square, Rectangle and Triangle. So you may define new types with new method called
Area()
, like this:type Circle struct { Radius float64 } func (t Circle) Area() float64 { return math.Pi * t.Radius * t.Radius }
And for
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
:type Rectangle struct { A, B float64 } func (t Rectangle) Area() float64 { return t.A * t.B }
And for
Square
:type Square struct { A float64 } func (t Square) Area() float64 { return t.A * t.A }
Here you are
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}, }
Interesting! How to collect them all in one place?
first you needShape 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}, }
After all
Circle
is aShape
andTriangle
is aShape
too.
Now you may print area of each shape using single statementv.Area()
:for _, v := range shapes { fmt.Println(v, "\tArea:", v.Area()) }
So
Area()
is common interface between all shapes. Now how to calculate and call uncommon method like angles of triangle using aboveshapes
: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 aboveshapes
: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 from compiler try to convertv
of typeShape
to typeTriangle
, so if it is successful theok
will betrue
otherwisefalse
, then if it is successful callt.Angles()
to calculate triangle three 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 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)
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.
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.
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)
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.
Edit:
Question: What does the assertionx.(T)
returns when T is aninterface{}
and not a concrete type?
Answer:asserts that x is not nil and that the value stored in x is of type T.
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{}) }
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 typeinterface {}
and notint
.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 }
这篇关于在Go中解释类型断言的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
-