Go “强制执行类型输入”;“通用”;带有空接口的代码

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

很抱歉标题不明确

我正在读这本书,我认为将Go中的示例作为一个学习练习来实现会很好,但是这本书使用Java作为其语言来描述Go中的代码

前几章中有一章讨论了设置一些核心数据类型/容器样式的类以便以后重用,但我在尝试将这些类转换为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)
}
这在原则上是有效的,因为我可以将物品添加到
包中
,并检查其大小和所有内容。然而,这也意味着以下代码是合法的

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())
}