原本我以为Go添加泛型就加个type注释就可以,刚读了一遍Go generic proposal,发现要考虑的很多
提案里用C++类比,很久没写,不怎么熟悉,我用Java举例子
如下Go代码
// This function is INVALID.
func Stringify(type T)(s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String()) // INVALID
}
return ret
}这份代码问题在于,v只是T类型,编译系统无法确定T类型含有String()方法,在Go中,全部的字段都会在编译时进行解析绑定,所以Go不允许上面的写法
<!--more-->这是泛型系统普遍存在的问题
如下Java代码
public static <T> String[] Stringify1(T[] s) {
String[] ret = null;
for(int idx = 0 ; idx <= s.length; idx ++) {
ret[idx] = s[idx].String();
}
return ret;
}你的String()方法根本过不了编译,无法解析String字段 那Java是如何解决这个问题的呢?有界类型参数(Bounded Type Parameters)
class StringClass<T> extends ArrayList<T> {
public String String() {
return null;
}
}
public class App {
public static <T extends StringClass<Integer>> String[] Stringify(T[] s) {
String[] ret = null;
for(int idx = 0 ; idx <= s.length; idx ++) {
ret[idx] = s[idx].String();
}
return ret;
}
}通过extends硬性限定T的类型范围,这样调用String()时可正确解析。 如果Go支持泛型,那必须顺带着解决 Bounded Type Parameters 的问题,标准的解决问题引入问题解决问题循环
Go借助现有的interface来做类型约束(type constraint)
type Stringer interface {
String() string
}
func Stringify(type T Stringer)(s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return ret
}- 运算符约束
由于T是不定类型,所以无法比较
// This function is INVALID.
func Smallest(type T)(s []T) T {
r := s[0] // panic if slice is empty
for _, v := range s[1:] {
if v < r { // INVALID
r = v
}
}
return r
}那Java呢?根据我所学,Java禁止不定类型的比较,C++支持,但需要运算符重载(operator overloading)
Java中原始类型(primitive type)都被封装到对象中,就连Integer比较都需要拆箱来提取int,更何谈直接比较Object
Go从设计上就没有OOP,struct传递完全可以用pointer这个生值,而这些值天生就是可以被比较的,不能禁止
这个要说一下
这个代码s也是传递引用,我在这里不想讨论值传递和引用传递,明白的人自然能看明白
public void print(String s) {}也就是s也是一个数值,不是对象,不过Java在你调用s之前会解引用(*s).balabalabala
type s struct {
}
func (this *s) doSth(){}
func print(this *s)){
// Go这里能调用,是由于编译器发现没有歧义,会帮助你转换成(*this).balabala调用
this.doSth()
}Go官方FAQ
Why not use the syntax F<T> like C++ and Java? When parsing code within a function, such as v := F<T>, at the point of seeing the < it's ambiguous whether we are seeing a type instantiation or an expression using the < operator. Resolving that requires effectively unbounded lookahead. In general we strive to keep the Go parser efficient.