Go “强制执行类型输入”;“通用”;带有空接口的代码
很抱歉标题不明确 我正在读这本书,我认为将Go中的示例作为一个学习练习来实现会很好,但是这本书使用Java作为其语言来描述Go中的代码 前几章中有一章讨论了设置一些核心数据类型/容器样式的类以便以后重用,但我在尝试将这些类转换为Go设置时遇到了困难,主要是因为这些数据类型似乎喜欢使用Java泛型 例如,我编写了以下代码Go “强制执行类型输入”;“通用”;带有空接口的代码,go,Go,很抱歉标题不明确 我正在读这本书,我认为将Go中的示例作为一个学习练习来实现会很好,但是这本书使用Java作为其语言来描述Go中的代码 前几章中有一章讨论了设置一些核心数据类型/容器样式的类以便以后重用,但我在尝试将这些类转换为Go设置时遇到了困难,主要是因为这些数据类型似乎喜欢使用Java泛型 例如,我编写了以下代码 package bag type T interface{} type Bag []T func (a *Bag) Add(t T) { *a = append(*a
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)
}
这在原则上是有效的,因为我可以将物品添加到包中
,并检查其大小和所有内容。然而,这也意味着以下代码是合法的
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>()
问题是类型强制仅在运行时强制
有人知道如何改进吗?我自己是新手,所以我很好奇是否有人会有更好的答案,但我是这样看的: 您需要编译时强制,即当对
IntSlice
调用Add()
时,其参数是int
。好吧,下面是你如何做到的:
func (i *IntSlice) Add(t int) {
*i = append(*i, t)
}
由于没有泛型,Add()
方法对于每种类型的Bag
都是不同的,因此Bag
接口(假设您需要它)变成:
type Bag interface {
IsEmpty() bool
Size() int
}
这对我来说是有道理的,因为你不能把一个包
传来传去,然后往里面扔任何东西。知道某个东西是一个包
不足以知道如何在它上面调用Add()
;你必须知道你在处理什么类型的包
您可以使接口特定于该类型,如IntBag
,但由于实际上只有一种类型满足该接口,因此您最好放弃该接口,并将IntSlice
的名称更改为IntBag
基本上,这意味着完全放弃任何泛型,比如,使用一些方法创建一个类型,这些方法可以实现您想要的:
type IntBag []int
func (b *IntBag) Add(i int) {
*b = append(*b, i)
}
func (b IntBag) IsEmpty() bool {
return len(b) == 0
}
func (b IntBag) Size() int {
return len(b)
}
更新:有时泛型真的会派上用场。在我看来,我们只能根据具体情况来选择对给定问题最合适的解决方案。使用空接口和反射,您可以获得一些类似泛型的行为,但它往往很难看,并且您放弃了一些编译时类型检查。或者你放弃了泛型而有一些代码重复。或者你只是用一种完全不同的方式
几周前我问了一个问题,关于我应该如何使用Go来处理我认为需要类层次结构的问题。答案基本上是没有普遍的解决办法;都是个案。我认为这同样适用于泛型:Go中没有泛型,也没有将基于泛型的解决方案转换为Go的通用解决方案
在许多情况下,您可能会在另一种语言中使用泛型,但接口在Go中是完全足够的(或真正出色)。这里的例子中,接口并不是一个真正合适的替代品。另请参见:。我对围棋非常精通。泛型是一个热门话题,目前没有类似java泛型或C++模板的主题。目前的惯例是使用空接口实现“泛型”类型,然后使用特定的类型实现包装它,确保只使用该类型的元素。下面是一个来自Go标准库的容器/list
示例
谢谢你的回复。我能看到的唯一问题是,如果要在任何函数中使用Add
方法,而该函数将Bag
接口作为参数(它将不起作用)但我认为这是您必须与此解决方案进行的权衡……因此,如果您想使其非通用,您需要创建一个用于处理IntBag
的函数,另一个用于处理StringBag
的函数,等等,除非您创建了某种“dispatcher”反映在行李上并根据类型分配呼叫的入口点。这仍然意味着要编写n
函数though@djhworld我在我的答案底部添加了一个更新以回应你的评论。我已经接受了你的答案,我想我可能不得不放弃在围棋中做算法书练习的任务,但是哦,好吧。无论如何谢谢你man@djhworld我相信,如果你下定决心,你仍然可以在围棋中通读这本书,但事实上,为了专注于学习材料而不是处理语言差异,使用Java可能更简单。
type Bag interface {
IsEmpty() bool
Size() int
}
type IntBag []int
func (b *IntBag) Add(i int) {
*b = append(*b, i)
}
func (b IntBag) IsEmpty() bool {
return len(b) == 0
}
func (b IntBag) Size() int {
return len(b)
}
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())
}