对于模棱两可的标题感到抱歉。
我正在阅读本书http://algs4.cs.princeton.edu/home/,我认为将Go中的示例作为一种学习练习来实现是好的,但本书使用Java作为其语言来描述代码。
第一章的其中一章讨论了设置一些核心数据类型/容器样式类以便以后重用,但是我在尝试将这些数据转换为Go设置时遇到了麻烦,主要是因为这些数据类型似乎正在享受使用Java泛型。
例如,我编写了以下代码
package bag
type T interface{}
type Bag []T
func (a *Bag) Add(t T) {
*a = append(*a, t)
}
func (a *Bag) IsEmpty() bool {
return len(*a) == 0
}
func (a *Bag) Size() int {
return len(*a)
}
原则上我可以将项目添加到Bag
并检查其大小和所有内容。但是,这也意味着以下代码是合法的
a := make(bag.Bag,0,0)
a.Add(1)
a.Add("Hello world!")
a.Add(5.6)
a.Add(time.Now())
我只是想知道是否有任何强制执行类型的方式,因此它符合类似于
的合同Bag<T> bagOfMyType = new Bag<T>()
e.g。
Bag<Integer> bagOfInts = new Bag<Integer>()
我知道Go没有泛型,他们不是真正的The Go Way,但我只是想知道是否有可能在编译时“强制执行”任何事情(可能不是)
对不起,很长的帖子
编辑:好的,所以我一直在深入研究这个问题,我几乎放弃了仿制药的一面(我明白这不是Go的路线图)所以我正在考虑做一些类似于带接口的Haskell类型类,比如
type T interface{}
type Bag interface {
Add(t T)
IsEmpty() bool
Size() int
}
type IntSlice []int
func (i *IntSlice) Add(t T) {
*i = append(*i, t.(int)) // will throw runtime exception if user attempts to add anything other than int
}
func (i *IntSlice) IsEmpty() bool {
return len(*i) == 0
}
func (i *IntSlice) Size() int {
return len(*i)
}
此问题是类型强制仅在运行时强制执行。
任何人都有任何想法如何改善这个?
答案 0 :(得分:5)
我是Go的新手,所以我很好奇是否有人会有更好的答案,但这就是我的看法:
您希望编译时强制执行,在Add()
上调用IntSlice
时,其参数为int
。好吧,这就是你如何做到的:
func (i *IntSlice) Add(t int) {
*i = append(*i, t)
}
由于没有泛型,Add()
方法对于每种类型的Bag
都会有所不同,因此Bag
接口(假设您需要它)变为:< / p>
type Bag interface {
IsEmpty() bool
Size() int
}
这对我来说很有意义,因为你不能传递Bag
并在其中扔任何东西。知道某些内容是Bag
并不足以知道如何在其上调用Add()
;你必须知道你正在处理的Bag
是什么类型。
你可以使接口特定于类型,如IntBag
,但由于只有一种类型实际上会满足该接口,你也可以摆脱接口并更改{{1的名称转到IntSlice
。
基本上这意味着完全放弃任何类似通用的东西,只是创建一个类型,其中包含一些你想要的方法:
IntBag
更新:有时候仿制药真的会派上用场。在我看来,我们仍然会根据具体情况选择对于特定问题最恰当的选择。使用空接口和反射,您可以获得一些类似通用的行为,但它往往是丑陋的,您放弃了一些编译时类型检查。或者你放弃了泛型并且有一些代码重复。或者你只是采用完全不同的方式。
几周前我问了一个关于我应该如何使用Go处理我认为需要类层次结构的问题的问题。答案基本上是没有通用的解决方案;这完全是个案。我认为同样适用于泛型:Go中没有泛型,并且没有通用的解决方案将基于泛型的解决方案转换为Go。在很多情况下,您可能会在另一种语言中使用泛型,但在Go中界面完全足够(或真正闪耀)。您的示例是接口不是真正替代的示例。另见:Go Vs. Generics。
答案 1 :(得分:1)
我非常精通Go。泛型是一个备受争议的话题,目前没有类似于Java泛型或C ++模板的东西。目前的惯例是实施一个&#34;泛型&#34;使用空接口键入,然后使用特定类型实现将其包装,以确保仅使用该类型的元素。以下是Go标准库中container/list
的示例。
http://play.golang.org/p/9w9H1EPHKR
package main
import (
"container/list"
"fmt"
)
type IntList struct {
innerList *list.List
}
func NewIntList() *IntList {
return &IntList{list.New()}
}
func (l *IntList) Add(i int) {
// this is the only way to add an element to the list,
// and the Add() method only takes ints, so only ints
// can be added
l.innerList.PushBack(i)
}
func (l *IntList) Last() int {
lastElem := l.innerList.Back()
// We can safely type-assert to an int, because Add()
// guarantees that we can't put a non-int into our list
return lastElem.Value.(int)
}
func main() {
l := NewIntList()
l.Add(5)
l.Add(4)
l.Add(3)
l.Add(2)
l.Add(1)
fmt.Println("Expecting 1; got:", l.Last())
}