duan4523 2012-08-10 19:30
浏览 16
已采纳

带有空接口的“通用”代码中的强制类型

Sorry for the ambiguous title.

I'm reading this book http://algs4.cs.princeton.edu/home/ and I thought it would be good to implement the examples in Go as a learning exercise, however the book uses Java as its language to describe code in.

One of the first chapters discusses setting up some core datatypes/container style classes to re-use later on but I'm having trouble trying to hammer these into a Go setting, mainly because these datatypes seem to be enjoying the use of Java generics.

For example, I've written the following code

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

This works in principle in the sense that I can add items to a Bag and check its size and everything. However this also means that the following code is legal

a := make(bag.Bag,0,0)
a.Add(1)
a.Add("Hello world!")
a.Add(5.6)
a.Add(time.Now())

I was just wondering if there was any way of enforcing the type so it conforms to a contract similar to

Bag<T> bagOfMyType = new Bag<T>()

e.g.

Bag<Integer> bagOfInts = new Bag<Integer>()

I know Go doesn't have generics and they're not really The Go Way, but I was just wondering if it is a possible to "enforce" anything at compile time (probably not)

Sorry for the long post

EDIT: OK so I've been looking into this a little further, I've pretty much given up with the generics side of things (I understand this is not on the roadmap for Go) so I'm thinking of doing something similar to Haskell typeclasses with interfaces, e.g.

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

The problem with this is the type enforcement is only enforced at runtime.

Anyone got any ideas how to improve on this?

  • 写回答

2条回答 默认 最新

  • dongwu8064 2012-08-10 20:52
    关注

    I'm new to Go myself, so I'm curious if someone will have a better answer, but here's how I see it:

    You want compile-time enforcement that when Add() is called on an IntSlice, its parameter is an int. Well, here's how you do that:

    func (i *IntSlice) Add(t int) {
        *i = append(*i, t)
    }
    

    Since there aren't generics, the Add() method is going to be different for every type of Bag, so the Bag interface, assuming you need it, becomes just:

    type Bag interface {
        IsEmpty() bool
        Size() int
    }
    

    That makes sense to me, because you can't pass a Bag around and throw just anything in it. Knowing that something is a Bag isn't enough to know how to call Add() on it; you must know what type of Bag you're dealing with.

    You could make the interface specific to the type, like IntBag, but since only one type is actually going to satisfy that interface, you might as well get rid of the interface and change the name of IntSlice to IntBag.

    Basically that means giving up entirely on anything generic-like, and just creating a type with some methods that do what you want:

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

    Update: Sometimes generics really would come in handy. It seems to me we're left choosing on a case-by-case basis what exactly is best for a given problem. With empty interfaces and reflection, you can get some generic-like behavior, but it tends to be ugly and you give up some compile-time type checking. Or you give up on generics and have some code-duplication. Or you just do it a totally different way.

    I asked a question a few weeks ago about how I should use Go to handle problems that look to me like they need class hierarchies. The answer was basically that there is no general solution; it's all case-by-case. I think the same applies for generics: there are no generics in Go, and there's no general solution for translating generics-based solutions to Go.

    There are many cases where you might use generics in another language but interfaces are perfectly adequate (or truly shine) in Go. Your example here is one where interfaces aren't really a proper replacement. See also: Go Vs. Generics.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 矩阵加法的规则是两个矩阵中对应位置的数的绝对值进行加和
  • ¥15 活动选择题。最多可以参加几个项目?
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型
  • ¥15 vs2019中数据导出问题
  • ¥20 云服务Linux系统TCP-MSS值修改?
  • ¥20 关于#单片机#的问题:项目:使用模拟iic与ov2640通讯环境:F407问题:读取的ID号总是0xff,自己调了调发现在读从机数据时,SDA线上并未有信号变化(语言-c语言)
  • ¥20 怎么在stm32门禁成品上增加查询记录功能
  • ¥15 Source insight编写代码后使用CCS5.2版本import之后,代码跳到注释行里面
  • ¥50 NT4.0系统 STOP:0X0000007B
  • ¥15 想问一下stata17中这段代码哪里有问题呀