在研究泛型时,我注意到泛型方法和泛型类型(类或接口)之间的类型引入语法的不同使我感到困惑.
泛型方法的语法是
<T> void doStuff(T t) {
// Do stuff with T
}
Run Code Online (Sandbox Code Playgroud)
文档说
泛型方法的语法包括一个类型参数,在尖括号内,并出现在方法的返回类型之前
泛型类型的语法是
class Stuff<T> {
// Do stuff with T
T t;
}
Run Code Online (Sandbox Code Playgroud)
文档说
由尖括号(<>)分隔的类型参数部分跟在类名后面.它指定了类型参数
因为它既没有说明为什么它必须在之前或之后.
为了保持彼此一致,我期望方法语法
void doStuff<T>(T t) {}或类型语法(对于类)class <T>Stuff {},但显然不是这种情况.
为什么之前必须引入一个,另一个之后呢?
我使用泛型主要是以形式出现List<String>并且认为<String>List可能看起来很奇怪,但这是一个主观论证,除了方法之外它也是如此.您可以拨打doStuff像
this.<String>doStuff("a string");
寻找技术解释我认为可能<T>必须在指定返回类型之前引入方法,因为T可能是返回类型,编译器可能无法像那样向前看,但这听起来很奇怪,因为编译器很聪明.
我认为除了"语言设计师就是这样做"之外,还有一个解释,但我找不到它.
Fra*_*man 17
答案确实存在于已经链接的GJ规范中,引自该文件,第14页:
通过解析约束使得在方法名称之前传递参数的约定是必要的:使用更常规的"方法名称之后的类型参数"约定,表达式
f (a<b,c>(d))将具有两个可能的解析.
f(a<b,c>(d))可以解析为f(a < b, c > d) (传递给f的比较中的两个布尔值)或f(a<B, C>(d)) (带有类型参数B和C的值调用以及传递给f的值参数d).我认为这也可能是为什么Scala选择使用[]而不是<>用于泛型.
我强烈的假设是,因为正如你所说的方法,泛型参数也可以是函数的返回类型:
public <RETURN_TYPE> RETURN_TYPE getResult();
Run Code Online (Sandbox Code Playgroud)
所以当编译器到达函数的返回类型时,它的类型已经被遇到(例如,它知道它是泛型类型).
如果你有一个像这样的语法
public RETURN_TYPE getResult<RETURN_TYPE>();
Run Code Online (Sandbox Code Playgroud)
它需要第二次扫描才能解析.
对于类,这不是问题,因为对泛型类型的所有引用都出现在类定义块中,即在声明泛型类型之后.
对此没有一些深刻的理论原因 - 这似乎是"语言设计者就是这样做的".例如,C#确实使用了你想知道为什么Java没有实现的语法.以下代码:
private T Test<T>(T abc)
{
throw new NotImplementedException();
}
Run Code Online (Sandbox Code Playgroud)
将编译.C#与Java类似,这意味着Java没有理论上的原因也不能实现同样的东西(特别是考虑到两种语言在开发的早期都实现了泛型).
现在Java语法的优点是使用当前语法为方法实现LL(1)解析器稍微容易一些.