Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

读Go泛型提案有感 #45

Open
taikulawo opened this issue Jun 25, 2020 · 0 comments
Open

读Go泛型提案有感 #45

taikulawo opened this issue Jun 25, 2020 · 0 comments

Comments

@taikulawo
Copy link
Collaborator

taikulawo commented Jun 25, 2020

原本我以为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不允许上面的写法

这是泛型系统普遍存在的问题

如下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 like C++ and Java?
When parsing code within a function, such as v := F, 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant