读Go泛型提案有感

作者:taikulawo创建时间:2020-06-25字数统计:2130预计阅读需要5分钟

#Go#基础

原本我以为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
}
  1. 运算符约束
由于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.

0 comments
Anonymous
Markdown is supported

Be the first guy leaving a comment!