解释 Go 中的类型断言 [英] Explain Type Assertions in Go

查看:30
本文介绍了解释 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 that x is not nil and that the value stored in x is of type T.

我为什么要使用它们:

  • 检查 x 是否为零
  • 检查它是否可以转换(断言)为另一种类型
  • 转换(断言)为另一种类型
    • t := x.(T) => t 是 T 类型;如果 x 为 nil,它会崩溃.

    • t := x.(T) => t is of type T; if x is nil, it panics.

    t,ok := x.(T) => 如果 x 为 nil 或不是 T 类型 =>okfalse 否则 oktrue 并且 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 是一个 ShapeTriangle 也是一个 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
    

    注意:这里的 cinterface {} 而不是 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆