Golang中如何结合结构体和接口理解泛型的使用

Golang中如何结合结构体和接口理解泛型的使用 你好!

我正在学习泛型,并尝试将所有内容整合起来,但我似乎无法理解它。我能否使用联合类型并通过接口访问结构体内部的属性?

谢谢!

type rectangle struct {
	length, height float64
}

type square struct {
	length, height float64
}

type Shape interface {
	rectangle | square
}

func getArea[T Shape](shape T) float64 {
	return shape.length * shape.height
}

func main() {
	rect := rectangle{1.3, 4.3}
	sqr := square{3.2, 3.4}

	fmt.Println(getArea(rect))
	getArea(sqr)
}

编译器错误:

# command-line-arguments
./main.go:83:15: shape.length undefined (type T has no field or method length)
./main.go:83:30: shape.height undefined (type T has no field or method height)

更多关于Golang中如何结合结构体和接口理解泛型的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

如果这也需要适用于非矩形图形,我认为 getArea 不应再是一个独立的函数,而应放在 Shape 接口上。否则,每当需要添加新形状时(例如椭圆、其他多边形,或带/不带曲线的“自由形式”形状等),你都需要修改 getArea 的实现。如果将 getArea 移到 Shape 上的一个 Area 函数中,你不仅不再需要 getArea,也不再需要 isCircleGo Playground - The Go Programming Language

func main() {
    fmt.Println("hello world")
}

更多关于Golang中如何结合结构体和接口理解泛型的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


嗨 Sean,

非常感谢您的回复。那个 Go 游乐场超级有帮助!我来自 TypeScript 领域,所以确实很不一样。我最初的想法是,如果我有一个 Shape,比如圆形、正方形、矩形等等,然后它们会有某种过滤功能?这显然是一个随意的例子,但我试图理解接口和结构体可以实现什么。

所以我最初的例子是这样的。

type rectangle struct {
	length, height float64
}

type square struct {
	length, height float64
}

type circle struct {
	radius float64
}

type Shape interface {
	rectangle | square | circle
}

func isCircle[T Shape](x T) bool {
	_, ok := interface{}(x).(circle)
	return ok
}

func getArea[T Shape](shape T) float64 {
	if isCircle(shape) {
		return 2 * math.Pi * shape.radius
	}
	return shape.length * shape.height
}

func main() {
	rect := rectangle{1.3, 4.3}
	sqr := square{3.2, 3.4}
	circ := circle{3.2}

	getArea(rect)
	getArea(sqr)
	getArea(circ)
}

虽然我很喜欢您的实现方式,但我的例子引入了一些代码异味 😅 它确实感觉更冗长,但那样代码会更清晰。

再次感谢!

你好,Tauqueer,欢迎来到论坛!

Go 语言中的接口只是方法集;结构体字段不属于接口的一部分,因此你无法以那种方式编写通用的 getArea 函数。

在你的例子中,我实际上没有看到泛型的使用场景:只用接口就可以了。Shape 可以重新定义为一个包含 LengthHeight 方法的接口,getArea 函数就可以使用这些方法:Go Playground - The Go Programming Language

至于尝试学习泛型,我不确定我有什么有用的指导原则。我想在我自己的代码中,有两个地方我写了使用泛型的代码:

  1. workers/workers.go at 0af79d6a76499b1b0cbd9521b1cb55977f0fe4bd · skillian/workers · GitHub
  2. expr/stream.go at da8a0af9071142cdbb3196a74e36de06333fd16a · skillian/expr · GitHub

第一个例子可能对你有用,可以看看它是如何工作的:Work 函数接收两个主要参数:

  1. 一个某种泛型类型的通道(可能是一个参数结构体,或者更简单的类型如字符串等),
  2. 一个 worker 函数,它处理来自通道的一个元素并返回一个结果。

给定这两样东西,Work 函数返回一个结果通道,其类型由 worker 函数决定。

我注意到我没有任何测试来演示它是如何工作的…

Each 函数更针对我正在进行的另一个项目,所以它可能不如 workers 例子对你有用。我把它包括进来只是为了说明,至少对我来说,泛型并不是超级有用。

在Go中,类型参数约束不能直接用于访问结构体字段。你需要定义一个包含所需方法的接口,然后使用这个接口作为约束。以下是修正后的代码:

package main

import "fmt"

type rectangle struct {
    length, height float64
}

type square struct {
    length, height float64
}

// 定义接口而不是类型约束
type Shape interface {
    Area() float64
}

// 为rectangle实现Area方法
func (r rectangle) Area() float64 {
    return r.length * r.height
}

// 为square实现Area方法
func (s square) Area() float64 {
    return s.length * s.height
}

// 使用接口作为泛型约束
func getArea[T Shape](shape T) float64 {
    return shape.Area()
}

func main() {
    rect := rectangle{1.3, 4.3}
    sqr := square{3.2, 3.4}

    fmt.Println(getArea(rect)) // 输出: 5.59
    fmt.Println(getArea(sqr))  // 输出: 10.88
}

如果你确实需要直接访问字段,可以使用结构体类型约束并定义方法:

package main

import "fmt"

type rectangle struct {
    length, height float64
}

type square struct {
    length, height float64
}

// 定义类型约束
type Shape interface {
    rectangle | square
}

// 为每个类型实现Area方法
func (r rectangle) Area() float64 {
    return r.length * r.height
}

func (s square) Area() float64 {
    return s.length * s.height
}

// 使用接口组合约束
type AreaCalculator interface {
    Shape
    Area() float64
}

func getArea[T AreaCalculator](shape T) float64 {
    return shape.Area()
}

func main() {
    rect := rectangle{1.3, 4.3}
    sqr := square{3.2, 3.4}

    fmt.Println(getArea(rect)) // 输出: 5.59
    fmt.Println(getArea(sqr))  // 输出: 10.88
}

对于需要直接字段访问的场景,可以使用类型断言:

package main

import "fmt"

type rectangle struct {
    length, height float64
}

type square struct {
    length, height float64
}

type Shape interface {
    rectangle | square
}

func getArea[T Shape](shape T) float64 {
    // 使用类型switch访问字段
    switch s := any(shape).(type) {
    case rectangle:
        return s.length * s.height
    case square:
        return s.length * s.height
    default:
        return 0
    }
}

func main() {
    rect := rectangle{1.3, 4.3}
    sqr := square{3.2, 3.4}

    fmt.Println(getArea(rect)) // 输出: 5.59
    fmt.Println(getArea(sqr))  // 输出: 10.88
}
回到顶部