gpt4 book ai didi

go - 解释 Go 中的类型断言

转载 作者:数据小太阳 更新时间:2023-10-29 03:41:34 25 4
gpt4 key购买 nike

我正在阅读 The Go Programming Language 中的类型断言 x.(T)并且不理解它们。

我知道有不同的场景:

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

这是我不明白的:

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

我也用谷歌搜索过这个话题,但还是不明白。

最佳答案

简答

一行:

x.(T) asserts that x is not nil and that the value stored in x is of type T.

我为什么要使用它们:

  • 检查 x 是否为 nil
  • 检查接口(interface) x 持有的动态类型是什么
  • x中提取动态类型

他们究竟返回了什么:

  • t := x.(T) => t 是 T 类型;如果 x 为 nil,它会崩溃。

  • t,ok := x.(T) => 如果 x 是 nil 或不是 T 类型 => okfalse 否则 oktrue 并且 tT 类型。


详细解释

假设您需要计算 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 形状的 slice 中:

type Shape interface {
Area() float64
}

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

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

毕竟,Circle 是一个 ShapeTriangle 也是一个 Shape
现在您可以使用单个语句 v.Area() 打印每个形状的面积:

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

所以 Area() 是所有形状之间的通用接口(interface)。现在,我们如何使用上面的 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) 请求类型断言,这意味着我们要求编译器尝试将 v 类型的 Shape 转换为类型 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
}

另见:

Type assertions

For an expression x of interface type and a type T, the primaryexpression

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 thedynamic type of x is identical to the type T. In this case, T mustimplement the (interface) type of x; otherwise the type assertion isinvalid since it is not possible for x to store a value of type T. IfT is an interface type, x.(T) asserts that the dynamic type of ximplements the interface T.

If the type assertion holds, the value of the expression is the valuestored in x and its type is T. If the type assertion is false, arun-time panic occurs. In other words, even though the dynamic type ofx is known only at run time, the type of x.(T) is known to be T in acorrect 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 thespecial 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 ifthe assertion holds. Otherwise it is false and the value of v is thezero value for type T. No run-time panic occurs in this case.


编辑

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

It asserts that x is not nil and that the value stored in x is of type T.

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

package main

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

这有效(运行: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


请参阅带有注释输出的工作示例代码:
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 - 解释 Go 中的类型断言,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58052684/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com