Golang中实现多态结构体字段的方法

Golang中实现多态结构体字段的方法 (刚接触Go语言)

我想创建一个结构体字段,它可以持有指向两种不同类型结构体的指针。示例:

type Letter struct {
    ...
    Previous *Character // 这是无效的,我没有这个类型,这只是一个示例
}

type Punct struct {
    ...
    Previous *Character // 这是无效的,我没有这个类型,这只是一个示例
}

l1 := Letter{}
l2 := Letter{ Previous: &l1, }
p1 := Punct{ Previous: &l2, }
p2 := Punct{ Previous: &p1, }

这个想法(简化版)是:一个字符串由字符组成,字符可以是字母或标点符号(不,我不能在这里使用rune类型,因为每个结构体都包含一些我需要与每个字符关联的其他有用信息)。每个字符都有一个指向前一个字符的引用——因此有了Previous字段。如你所见,在每个结构体中,Previous应该被赋值为指向不同类型结构体或相同类型结构体的指针。

但我不确定在这里如何处理多态性,以及Previous字段应该声明为什么类型。接口在这里似乎没有意义,因为它们处理的是方法,而不是字段类型。你会如何处理这个问题?


更多关于Golang中实现多态结构体字段的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

能否简要概述一下如何使用接口来实现?

更多关于Golang中实现多态结构体字段的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


虽然可以通过接口实现期望的结果,但可能需要调用断言类型。

您可以使用泛型来实现相同的功能,而无需使用 any

// 示例代码

泛型?这是一个不错的方法,但你也需要为这个场景定义一个接口作为约束。如果这只是一个示例,我可能不会使用泛型,而是尝试将其泛化和抽象成接口,在我看来这样更具可扩展性。当然,具体问题需要具体分析,这只是我的个人观点。

哦,谢谢,这看起来非常明显。唯一的问题是,它的扩展性不太好,并且我决定添加的每个新结构体都需要在 Character 结构体中添加一个额外的字段。我想知道是否有其他方法可以实现相同的目标(我希望可能只是我对接口的理解不足,存在一个涉及接口的解决方案)。

type Character sturct {
    Pre *Character
    Letter * Letter
    Punct * Punct
}
l1:=Character{Letter:&Letter{}}
l2:=Character{Pre:&l1,Letter:&Letter{}}
p1:=Character{Pre:&l2,Punct:&Punct{}}
p2:=Character{Pre:&p1,Punct:&Punct{}}

应该将什么视为一个单一的元素?至于这个单元格是字母还是符号,这是该单元的一个属性。

type Character interface {
    Pre()Character
    ...
}
type Letter struct {
    Previous Character
}
func(l *Letter)Pre()Character{
    returen l.Previous
}

type Punct struct {
    Previous Character
}
func(p *Punct)Pre()Character{
    returen p.Previous
}

我认为你并不理解接口是什么。 any只是一个简单的接口类型,我这里只是用any来举例。实际上,你应该做的是类似这样的事情。

type Letter struct {
    Previous any
}

type Punct struct {
    Previous any
}

l1 := Letter{}
l2 := Letter{ Previous: &l1, }
p1 := Punct{ Previous: &l2, }
p2 := Punct{ Previous: &p1, }
// 像这样,但要调用它,必须像这样:
l,ok:=p1.Previous.(*Letter)
if ok{
}

这只是一个使用 any 来表示接口的简单示例,实际上会添加一些特定的方法来避免其他结构体类型实现该接口,从而造成代码混乱。

是的。我考虑过使用 any,虽然那似乎有点宽泛。我不需要对类型特别严格,但我想,如果能理解如何在 Go 中实现类似 OOP 风格的多态和继承,也不会有什么坏处,就像这样(伪 OOP 代码):

class Character { ... }
class Letter extends Character {
    previous Character 
}
class Punct extends Character {
    previous Character
}
l1 = new Letter();
l2 = new Letter();
p1 = new Punct();
p2 = new Punct();
l2.previous = l1; 
p1.previous = l2;
p2.previous = p1;

这段代码将 previous 字段限制为 Character 派生类的实例。

在Go中实现多态结构体字段的典型方法是使用接口。虽然你提到接口通常用于方法,但它们同样可以定义字段类型,因为接口类型可以持有任何实现了该接口的值。以下是解决方案:

首先定义一个包含所需方法的接口,然后让结构体实现该接口:

type Character interface {
    GetType() string
    // 可以添加其他需要的方法
}

type Letter struct {
    Content  string
    Previous Character // 使用接口类型
}

type Punct struct {
    Symbol   string
    Previous Character // 使用接口类型
}

// 实现Character接口的方法
func (l Letter) GetType() string {
    return "letter"
}

func (p Punct) GetType() string {
    return "punct"
}

现在你可以创建链式结构:

l1 := Letter{Content: "A"}
l2 := Letter{Content: "B", Previous: l1}
p1 := Punct{Symbol: "!", Previous: l2}
p2 := Punct{Symbol: "?", Previous: p1}

如果需要存储指针,可以这样调整:

type Letter struct {
    Content  string
    Previous Character
}

type Punct struct {
    Symbol   string
    Previous Character
}

l1 := &Letter{Content: "A"}
l2 := &Letter{Content: "B", Previous: l1}
p1 := &Punct{Symbol: "!", Previous: l2}
p2 := &Punct{Symbol: "?", Previous: p1}

如果需要访问具体类型,可以使用类型断言:

func processPrevious(char Character) {
    switch v := char.(type) {
    case *Letter:
        fmt.Printf("Previous is Letter: %s\n", v.Content)
    case *Punct:
        fmt.Printf("Previous is Punct: %s\n", v.Symbol)
    default:
        fmt.Println("Unknown type")
    }
}

// 使用
processPrevious(p2.Previous) // 输出: Previous is Punct: !

如果不需要方法,可以使用空接口interface{}(或Go 1.18+的any):

type Letter struct {
    Content  string
    Previous any
}

type Punct struct {
    Symbol   string
    Previous any
}

l1 := &Letter{Content: "A"}
l2 := &Letter{Content: "B", Previous: l1}
p1 := &Punct{Symbol: "!", Previous: l2}
p2 := &Punct{Symbol: "?", Previous: p1}

但推荐使用具体接口,因为它提供了类型安全。

回到顶部