猿问

使用接口为任意类型创建队列

作为学习 Go 的练习,我正在编写一个基本的队列数据结构。我昨天开始学习接口,我认为在这个练习中尝试使用它们会很酷。我想要完成的是拥有一个Queue可以接受实现此接口的任何类型:


type Queuable interface {

  Next() *Queuable  // This is probably not right

}

基本上我想要的是能够将任何具有Next()方法的类型添加到我的Queue. 所以我尝试的是:


type Node struct {

    value interface{}

    next  *Queuable

}


// Next gets the next object

func (n *Node) Next() *Queuable {

    return n.next

}


// Job - A job for the queue

type Job struct {

    instruction string

    next        *Queuable

}


// Next gets the next object

func (j *Job) Next() *Queuable {

    return j.next

}


// Queue ...

type Queue struct {

    head *Queuable

    size int

}

我的方法看起来像:


func (q *Queue) Enqueue(node *Queuable) {

    ...

}


// Dequeue - Remove a Queueable form the Queue

func (q *Queue) Dequeue() *Queuable {

  result := q.head

  q.head = q.head.Next()

  q.size--

  return result

}

我收到了很多这样的错误(基本上在任何有任务的行上):


current.Next undefined (type *Queuable is pointer to interface, not interface)

所以最终我想做的是:


func main() {

  queue := NewQueue()  // Helper function not pictured

  job := &Job{"some instructions", nil}

  node := &Node{5, nil}

  queue.Enqueue(node)  // queue = [node]

  queue.Enqueue(job) // queue = [node, job]

  queue.Dequeue() // node

  queue.Dequeue() // job

}


陪伴而非守候
浏览 174回答 2
2回答

蛊毒传说

不要使用指向接口类型的指针,只使用接口类型。Queuable是一种接口类型,因此在您使用 的代码中的任何地方*Queuable,都将其更改为Queuable. 例如:type Queuable interface {    Next() Queuable}type Node struct {    value interface{}    next  Queuable}// Next gets the next objectfunc (n *Node) Next() Queuable {    return n.next}...在 Go 中,接口类型的值存储一对:分配给变量的具体值,以及该值的类型描述符。更多关于接口的内部结构:反射定律#接口的表示所以你几乎不需要一个指向接口的指针。一个接口包含一个键值对,其中键可以是一个指针。接口指针有意义的罕见情况是,如果您想修改传递给另一个函数的接口类型变量的值。在您的示例中,该类型*Job实现了Queuable因为它有一个带有接收器类型的方法*Job,因此在需要值的任何地方Queuable,*Job都可以使用值(并且Queuable将创建和使用类型的隐式接口值)。回到你的例子:您Queuable只定义了一种方法来获取队列中的下一个元素,但没有定义一种方法来将它排入队列,这将使该解决方案失去灵活性。单个Next()方法仅描述它是“排队的”,但它不是(必然)“可排队的”。为了排队,我还要添加另一种方法:SetNext(Queuable)type Queuable interface {    Next() Queuable    SetNext(Queuable)}它的实现Node可以是例如:func (n *Node) SetNext(q Queuable) { n.next = q }在Go Playground上试试。另请注意,Nodeand中有一些代码重复Job,即next字段Next()和SetNext()方法。我们可以创建一个基本节点实现,例如:type Base struct {    next Queuable}func (b *Base) Next() Queuable     { return b.next }func (b *Base) SetNext(q Queuable) { b.next = q }现在您可以将这种Base类型嵌入到将“继承”字段和方法的具体Node和Job实现中,因此您不必在和类型上定义任何这些。nextNext()SetNext()NodeJob这是Nodeand的完整实现,Job不需要其他任何东西:type Node struct {    *Base    value interface{}}type Job struct {    *Base    instruction string}在Go Playground上试试这个。

慕的地6264312

永远不要使用指向接口类型的指针,这已经是一个指针了!因此,要使代码正常工作,请更改*Queuableas Queuable。type Node struct {    value interface{}    next  Queuable}// Next gets the next objectfunc (n *Node) Next() Queuable {    return n.next}// Job - A job for the queuetype Job struct {    instruction string    next        Queuable}但是,您可以使用方法接收器作为指针,具体取决于结构的复杂性。虽然如果使用的 struct 类型很简单,您可以定义使用 struct 值的方法,这种方式在内存中分配一个新地址。如果您使用方法接收器作为指针,它将引用该结构已在内存中占用的地址。关于接收者的指针与值的规则是值方法可以在指针和值上调用,但指针方法只能在指针上调用。出现这个规则是因为指针方法可以修改接收者;在一个值上调用它们将导致该方法接收该值的副本,因此任何修改都将被丢弃。因此,该语言不允许这种错误。经验法则是,为了一致性,最好坚持将方法定义作为指针或方法定义作为整个接口实现中的值。
随时随地看视频慕课网APP

相关分类

Go
我要回答