为什么Java不支持二级泛型

9
长话短说:下面的操作为什么在Java中不可行?
public class Test<A<B>> {} // A and B both being generic parameters.

注意:目前我没有特定的使用场景,而只是想了解为什么不允许这样做。

起初,我认为编译器无法确定A是否可以接受泛型参数,因为在编译A后,由于类型擦除,泛型将不再存在。

但是,如果是这样的话,我们就无法在任何类上使用泛型。因此,我取出了一个泛型类的字节码,并发现有元数据表明它接受泛型。

public class com.Test<T> {
  public com.Test();
    Code:
       0: aload_0
       1: invokespecial #12                 // Method java/lang/Object."<init>":()V
       4: return
}

我做了一个快速搜索,SO证实编译的代码也将包含与泛型相关的元数据

那么为什么编译器不允许多级泛型?

允许多级泛型会有什么问题吗?这是一种限制吗?还是其他原因?


3
一种猜测是:这会使类型系统的规则变得太复杂,甚至连作者自己也无法完全理解。而且它可能非常没有用处——正如您自己指出的,因为您无法想出任何用例。 - Marko Topolnik
2个回答

8

假设这个类实际上正在编译:

public class Test<A<B>> { .. }

这意味着类的正确实例化应该是这样的:
new Test<Class<Integer>>()
//or
new Test<List<String>>()

以下代码是错误的(因为提供的类型参数不是泛型):

new Test<String>();
//or
new Test<Object>();

然而,类型参数不应该被限制为通用或非通用 - 它只应该包含有关要替换的类型的一些元信息(事实证明,在类型擦除后,这是类型)。类型擦除本身可能是不允许此类构造的另一个可能原因。再次考虑上面正确定义了Test类并且你有这个:
new Test<Class<Integer>>();

当类型擦除发生时,<A<B>> 应该被替换为 Class<Integer>,但是由于擦除,我们只能得到一个 Class(尽管在内部它会包含有关 Integer 类型的信息)- 期望一个 Class<Integer>,但提供一个 Class 是不正确的。
感谢您提出这个有趣的问题!

3
Scala将其称为高级类型。因此,这绝对是可行的抽象。据我所知,从未认真考虑过将其添加到Java中。
不幸的是,我找不到任何关于Scala高级类型的好入门文章。我能找到的最好的是原始论文Generics of a Higher Kind;以下是它的摘要:
“随着Java 5和C# 2.0的推出,第一阶段参数多态性在主流面向对象编程语言中以泛型的形式被引入。尽管泛型的一阶变体非常有用,但它也带来了一些限制:可以对类型进行抽象,但生成的类型构造器不能进行抽象。这可能会导致代码重复。我们通过允许类型构造器作为类型参数和抽象类型成员来消除了这个限制。本文介绍了由此产生的类型构造器多态性的设计和实现。此外,我们研究了这个特性与现有面向对象构造的交互方式,并展示了它如何使语言更加表达。”

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接