简体   繁体   English

在Go中解释类型断言

[英]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: 我知道有不同的场景:

  • T is a concrete type or an interface T是具体类型或接口
  • One (asserted value?) or two (ok) values can be returned 可以返回一个(断言值?)或两个(ok)值

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. 我也搜索了这个话题,仍然不明白。

Short answer 简短的回答

In one line: 在一行中:

x.(T) asserts that x is not nil and that the value stored in x is of type T . x.(T)断言x不是nil,而x存储的值是T类型。

Why would I use them: 我为什么要用它们:

  • to check x is nil 检查x是否为零
  • to check if it's convertible (assert) to another type 检查它是否可转换(断言)为另一种类型
  • convert (assert) to another type 将(断言)转换为另一种类型

What exactly they return: 究竟是什么回报:

  • 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否则oktruetT类型。


Detailed explanation 详细解释

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是一个ShapeTriangle也是一个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: 另见:

Type assertions 输入断言

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的零值。 在这种情况下,不会发生运行时混乱


EDIT 编辑

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM