当前位置: 首页 > 面试题库 >

在Go中解释类型断言

姚海
2023-03-14
问题内容

我正在阅读《 Go编程语言》x.(T)中的类型断言,但不理解它们。

我了解有不同的方案:

  • T是具体类型或接口
  • 可以返回一个(确定值?)或两个(确定)值

这是我不明白的:

  • 我为什么要使用它们?
  • 他们到底返回什么?

我也用谷歌搜索这个话题,但仍然不明白。


问题答案:

一行:

x.(T)断言这x不是nil并且存储的值x是type T

我为什么要使用它们:

  • 检查x为零
  • 检查它是否可转换(断言)为另一种类型
  • 转换(断言)为另一种类型

他们到底返回什么:

  • t := x.(T)=> t是类型T;如果x为零,则表示恐慌。

  • t,ok := x.(T)=>如果x是零或不类型T=> okfalse否则oktruet是类型的T

详细说明

假设您需要计算4种不同形状的面积:圆形,正方形,矩形和三角形。您可以使用称为的新方法来定义新类型Area(),如下所示:

type Circle struct {
    Radius float64
}
func (t Circle) Area() float64 {
    return math.Pi * t.Radius * t.Radius
}

对于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))
}

对于Rectangle

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,其侧面具有其他形状:

shapes := []Shape{
    Circle{1.0},
    Square{1.772453},
    Rectangle{5, 10},
    Triangle{10, 4, 7},
}

有趣!我们如何将它们全部收集在一个地方?
首先,您需要Shape interface将它们全部收集成一片形状[]Shape

type Shape interface {
    Area() float64
}

现在,您可以像这样收集它们:

shapes := []Shape{
    Circle{1.0},
    Square{1.772453},
    Rectangle{5, 10},
    Triangle{10, 4, 7},
}

毕竟,CircleShapeTriangleShape太。
现在,您可以使用单个语句打印每个形状的区域v.Area()

for _, v := range shapes {
    fmt.Println(v, "\tArea:", v.Area())
}

Area()所有形状之间的通用接口也是如此。现在,我们如何使用上述方法来计算和调用像三角形的角之类的不常见方法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
}

现在是时候Triangle从上方提取shapes

for _, v := range shapes {
    fmt.Println(v, "\tArea:", v.Area())
    if t, ok := v.(Triangle); ok {
        fmt.Println("Angles:", t.Angles())
    }
}

使用t, ok := v.(Triangle)我们要求的类型断言,这意味着我们要求编译器尝试将vtype 转换Shape为type
Triangle,以便如果成功,ok则将为true否则false,然后如果成功,则调用t.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]

以及整个工作示例代码:

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
}

另请参阅:

类型断言

对于具有接口类型和类型T的表达式x,主要表达式

x.(T)

断言x不为nil,并且x中存储的值的类型为T。符号x。(T)称为类型断言。

更准确地说,如果T不是接口类型,则x。(T)断言x的动态类型与T的类型相同。在这种情况下,T必须实现x的(接口)类型。否则,类型断言无效,因为x不可能存储类型T的值。如果T是接口类型,则x。(T)断言x的动态类型实现了接口T。

如果类型断言成立,则表达式的值为存储在x中的值,其类型为T。 如果类型断言为false,则会发生运行时恐慌。
换句话说,即使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

在特殊形式的赋值或初始化中使用的类型断言

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)

产生另一个无类型的布尔值。如果断言成立,则ok的值为true。否则为false,并且v的值为T类型的零值。 在这种情况下,不会发生运行时恐慌

编辑

问题x.(T)当T是an interface{}而不是具体类型时,断言返回什么?

它断言x不是nil,并且x中存储的值是T类型。

例如,此恐慌(编译:成功,运行:)panic: interface conversion: interface is nil, not interface {}

package main

func main() {
    var i interface{} // nil
    var _ = i.(interface{})
}

这可行(运行:确定):

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

请参阅以下带有注释输出的工作示例代码:

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
}


 类似资料:
  • 问题内容: 我是C ++ / Java程序员,在日常编程中碰巧使用的主要范例是OOP。在某个线程中,我读到一条评论,即Type类本质上比OOP更直观。有人可以用简单的词来解释类型类的概念,以便像我这样的OOP家伙可以理解吗? 问题答案: 首先,我总是非常怀疑这种程序结构更直观。编程是违反直觉的,并且总是会因为人们自然而然地根据特定情况而不是一般规则来思考。要更改此设置,需要培训和实践,也称为“编程

  • 类型断言(Type Assertion)是一个使用在接口值上的操作,用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型。 在Go语言中类型断言的语法格式如下: value, ok := x.(T) 其中,x 表示一个接口的类型,T 表示一个具体的类型(也可为接口类型)。 该断言表达式会返回 x 的值(也就是 value)和一个布尔值(也就是 ok),可根据该布尔值判断 x 是否为 T

  • 我从Antlr4语法生成了Go语言的解析器。 语法在这里:https://raw.githubusercontent.com/antlr/grammars-v4/master/solidity/solidity.g4 代码段: 会有什么问题?

  • 问题内容: 在Go中使用类型断言/类型切换作为运行时类型发现的方法有多慢? 我听说例如在C / C ++中,在运行时发现类型会降低性能。要绕过它,通常将类型成员添加到类中,以便可以与这些类型进行比较而不是强制转换。 我在整个www上都没有找到明确的答案。 这是我要问的一个示例- 与其他类型检查方法(如上面提到的或我不知道的其他方法)相比,这是否算 快 ? 问题答案: 编写基准测试来检查它很容易:h

  • 问题内容: 我写了以下函数: 我想向函数添加类型注释: 但是,我想明确定义返回的字典内的值 不能 为None。 有没有办法说“类型,除”或“除”以外的所有可能值? 问题答案: 假设您愿意在调用函数时修复键和值的类型,则可以使用泛型来使其明确。这还可能会允许的情况下是,但它使意图很清楚。请注意,由于存在差异问题,因此必须使用。但是,无论如何这是优选的。 使用此定义,可以正确地将可选类型转换为非可选类

  • 本文向大家介绍详解js类型判断,包括了详解js类型判断的使用技巧和注意事项,需要的朋友参考一下 js类型转换中typeof会将null也识别为object, 而且返回的类型比少,我们用Object.prototype.toString来实现 第一版 但是这样写,一个个去判断数组,函数,对象的话很麻烦,比较过程化 第二版 我们想用type(obj)的方式返回对应的类型字符串,因为typeof是小写,