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

报告相同问题?

悬赏问题

  • ¥30 YOLO检测微调结果p为1
  • ¥20 求快手直播间榜单匿名采集ID用户名简单能学会的
  • ¥15 DS18B20内部ADC模数转换器
  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题