我有几个自定义类型需要以相同的方式进行处理.这似乎是泛型的完美用途.在这个过程中,我需要调用类型实例上的方法,这些方法返回相同类型的不同实例,然后我需要调用那些返回的实例上的方法,这是我无法工作的.出于这个问题的目的,我构造了一组简单得多的类型和一个过程,它例证了我遇到的问题.

下面是一个没有泛型的工作示例,它显示了类型(CircleSquare)和一个过程(.Bigger().Smaller()),稍后我将try 将其抽象为泛型函数(online demo):

package main

import (
    "fmt"
)

type Circle struct{ r float64 }

func NewCircle(r float64) *Circle  { return &Circle{r: r} }
func (c *Circle) Radius() float64  { return c.r }
func (c *Circle) Bigger() *Circle  { return &Circle{r: c.r + 1} }
func (c *Circle) Smaller() *Circle { return &Circle{r: c.r - 1} }

type Square struct{ s float64 }

func NewSquare(s float64) *Square   { return &Square{s: s} }
func (s *Square) Side() float64     { return s.s }
func (s1 *Square) Bigger() *Square  { return &Square{s: s1.s + 1} }
func (s1 *Square) Smaller() *Square { return &Square{s: s1.s - 1} }

func main() {
    fmt.Println(NewCircle(3).Bigger().Smaller().Radius()) // prints 3
    fmt.Println(NewSquare(6).Bigger().Smaller().Side())   // prints 6
}

创建泛型函数的第一件事是定义类型约束:

type ShapeType interface {
    *Circle | *Square
}

我将把ShapeType传递给process方法,并且我需要能够调用ShapeType实例上的方法,因此我需要定义另一个类型约束来指定可以在ShapeType上调用的方法:

type Shape[ST ShapeType] interface {
    Bigger() ST
    Smaller() ST
}

有了这些,我可以编写一个process方法(online demo):

func process[ST ShapeType](s Shape[ST]) ST {
    return s.Bigger().Smaller()
}

然而,这无法编译,因为s.Bigger()的返回值是ST,而不是Shape[ST],所以Go不知道它可以在返回值s.Bigger()上调用Smaller().用Go的话说:

S.Bigger().Smaller未定义(类型ST没有更小的字段或方法)

如果Bigger()Smaller()没有返回它们的接收器类型的实例,我可以这样写:

type Shape interface {
    *Circle | *Square
    Bigger()
    Smaller()
}

func process[S Shape](x S) S {
    x.Bigger().Smaller()
    return x // I guess we wouldn't even have to return x, but just for example's sake
}

相反,我需要写下:

type Shape interface {
    *Circle | *Square
    Bigger() Shape
    Smaller() Shape
}

而且,围棋似乎不喜欢自引用类型的约束.

如果可以将具体类型断言/转换为它所遵循的接口,那么我可以让它工作,但似乎不可能做到这一点(online demo):

func process[ST ShapeType](s Shape[ST]) ST {
    s1 := s.Bigger()
    s2 := s1.(Shape[ST]) // go is not happy here
    return s2.Smaller()
}

对于这一点,Go说:

无法对类型参数值S1(由ShapeType约束的ST类型变量)使用类型断言

我不知道还能试什么.

是否可以使用泛型来处理这类类型?如果是这样的话,是如何做到的呢?

推荐答案

将您try 的两个接口组合在一起:

type Shape[ST any] interface {
    *Circle | *Square
    Bigger() ST
    Smaller() ST
}

然后用类型参数本身实例化约束process:

func process[ST Shape[ST]](s ST) ST {
    return s.Bigger().Smaller()
}
  • 将Union元素*Circle | *Square添加到Shape[ST any]中意味着只有这两种类型才能实现接口
  • 然后在方法签名中使用类型参数,如Bigger() ST,这意味着传递的任何类型都有一个返回自身的方法.

如果您希望保留ShapeType作为单独的接口,则可以将Shape写为:

type Shape[ST any] interface {
    ShapeType
    Bigger() ST
    Smaller() ST
}

您也可以将process方法与类型推理结合使用,没有任何问题:

func main() {
    c1 := NewCircle(3)
    c2 := process(c1) 
    fmt.Println(c2.Radius()) // prints 3 as expected
    fmt.Printf("%T\n", c2) // *main.Circle

    s1 := NewSquare(6)
    s2 := process(s1)
    fmt.Println(s2.Side()) // prints 6 as expected
    fmt.Printf("%T\n", s2) // *main.Square
}

最终操场:https://go.dev/play/p/_mR4wkxXupH

Go相关问答推荐

杜松子wine -戈尼克背景在 children 围棋例行公事中被取消

Go GORM创建表,但不创建列

是不是有什么原因导致`Strings.EqualFold`不先进行长度比较?

Golang内置打印(Ln)函数&S行为怪异

重新赋值变量时未清除动态类型-这是错误吗?

如果第一次匹配条件,如何跳过切片中的值

Go:为什么我不能比较 net.Addr

Golang:访问any类型泛型上的字段

从 wincrypt API 到 Go 的 RC2 解密

无法读取postman 中的表单数据

在 go 中,将接收器 struct 从值更改为指针是否向后兼容?

如何将 base64 编码的公钥转换为 crypto.PublicKey 或 ecdsa.PublicKey

级联调用泛型函数时的泛型类型推断

使用 oklog/run 来自 Go 编译器的错误(无值)用作值

vs 代码调试 go 测试不通过标志

Go Fyne 禁用 HSplit 调整大小?

将 Simple Go Web 应用程序部署到 Elastic Beanstalk

go mod tidy 错误消息:但是 go 1.16 会 Select

Scanner.Buffer - 最大值对自定义拆分没有影响?

如何允许可转换为指针的泛型类型参数化另一种可转换为指针的泛型类型?