如何在Go中为不同类型实现容器?

时间:2017-06-20 07:20:27

标签: generics go types interface

以下代码在Go中实现了一个int列表:

package main

import "fmt"

type List struct {
    Head int
    Tail *List
}

func tail(list List) *List {
    return list.Tail
}

func main() {
    list := List{Head: 1, Tail: 
         &List{Head: 2, Tail:
         &List{Head: 3, Tail:
         nil}}}
    fmt.Println(tail(list).Head)
}

问题是这仅适用于int。如果我想要一个strings列表,我需要再次重新实现每个列表方法(例如tail)!这显然不实用,所以,这可以通过使用空接口解决:

type List struct {
  Head interface{} // Now works for any type!
  Tail *List
}

问题是,1。由于类型转换,这似乎要慢得多,2。它抛弃了类型安全,允许人们键入检查任何东西:

// This type-checks!
func main() {
    list := List{Head: 123456789 , Tail:
         &List{Head: "covfefe" , Tail:
         &List{Head: nil       , Tail:
         &List{Head: []int{1,2}, Tail:
         nil}}}}
    fmt.Println(tail(list).Head)

显然,该程序应以静态类型语言进行类型检查。

如何实现一个List类型,它不需要我为每个包含的类型重新实现所有List方法,但是它能保持预期的类型安全性和性能?

3 个答案:

答案 0 :(得分:7)

Go doesn't have generic types,所以你坚持使用你列出的选项。遗憾。

  

同时,Go的内置映射和切片,以及使用空接口构造容器(具有显式拆箱)的能力意味着在许多情况下,可以编写执行泛型将启用的代码(如果不太顺利)。

如果您对要存储在容器中的元素有更多了解,可以使用更专业的接口类型(而不是空接口interface{}),

  • 可以帮助您避免使用type assertions保持良好表现
  • 仍然保持类型安全
  • 它可以用于(隐式)实现您的界面的所有类型(代码“可重用”,不需要复制多种类型)。

但那是关于它的。请在此处查看此示例:Why are interfaces needed in Golang?

另外,如果你错过了它,标准库已经在container/list包中有一个双向链表实现(对于值也使用interface{}类型)。

答案 1 :(得分:4)

确认我们希望通用类型更慢,而不是更快,这一点非常重要。优秀的fastutil Java库通过使用具体实现,优于标准库中更灵活的类。

Russ Cox(Go的作者之一)summarises the situation是这样的:

  
      
  • (C方法。)将它们排除在外。      
        
    • 这会减慢程序员的速度。
    •   
  •   
  • (C ++方法。)编译时专业化或宏扩展。      
        
    • 这会减慢编译速度。
    •   
  •   
  • (Java方法。)隐含地包装所有内容。      
        
    • 这会减慢执行速度。
    •   
  •   

您可能对this living document感兴趣,其中列出了许多优点和缺点。

正如另一个答案所指出的,Go并不支持您在此尝试实现的设计。就个人而言,我只是复制代码 - 它并不多,测试很便宜,大多数时候你实际上并不需要你想要实现的通用行为。

答案 2 :(得分:1)

@Output提到的那样,没有一种直截了当的方式。

您可以尝试使用code generation为新类型生成方法。