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 救!ENVI5.6深度学习初始化模型报错怎么办?
  • ¥30 eclipse开启服务后,网页无法打开
  • ¥30 雷达辐射源信号参考模型
  • ¥15 html+css+js如何实现这样子的效果?
  • ¥15 STM32单片机自主设计
  • ¥15 如何在node.js中或者java中给wav格式的音频编码成sil格式呢
  • ¥15 不小心不正规的开发公司导致不给我们y码,
  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000