为什么不能在具有多个边界的类型参数中使用类型参数?

2022-08-31 17:02:29

所以,我知道以下内容不起作用,但为什么它不起作用?

interface Adapter<E> {}

class Adaptulator<I> {
    <E, A extends I & Adapter<E>> void add(Class<E> extl, Class<A> intl) {
        addAdapterFactory(new AdapterFactory<E, A>(extl, intl));
    }
}

该方法给了我一个编译错误,“无法指定任何其他绑定适配器<E>当第一个绑定是类型参数时”(在Eclipse中),或“类型参数不能后跟其他边界”(在IDEA中),供您选择。add()

显然,你只是不允许在那里使用 type 参数,在 之前,就是这样。(在你问之前,如果你切换它们,它就不起作用了,因为不能保证它不是一个具体的类。但为什么不呢?我查看了Angelika Langer的常见问题解答,但找不到答案。I&I

通常,当某些泛型限制看起来是任意的时,这是因为您创建了一种类型系统实际上无法强制实施正确性的情况。但我不明白什么情况会破坏我在这里试图做的事情。我想说也许它与类型擦除后的方法调度有关,但只有一种方法,所以它不像有任何歧义......add()

有人可以为我演示问题吗?


答案 1

我也不确定为什么会有限制。你可以尝试向Java 5 Generics(主要是Gilad Bracha和Neal Gafter)的设计师发送一封友好的电子邮件。

我的猜测是,他们希望只支持绝对最小的交集类型(这本质上是多重边界),以使语言不比所需的更复杂。交集不能用作类型注释;程序员只有在交集显示为类型变量的上限时才能表示交集。

为什么这个案子甚至得到支持?答案是,多个边界允许您控制擦除,这允许在生成现有类时保持二进制兼容性。正如Naftalin和Wadler在书中第17.4节中所解释的那样,一种方法在逻辑上具有以下签名:max

public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll)

但是,这会擦除到:

public static Comparable max(Collection coll)

这与 的历史签名不匹配,并导致旧客户端中断。对于多个边界,擦除时仅考虑最左边的边界,因此如果给定以下签名:maxmax

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

然后,删除其签名将变为:

public static Object max(Collection coll)

这等于泛型之前的签名。max

Java设计人员只关心这个简单的情况,并限制交集类型的其他(更高级)使用,因为他们只是不确定它可能带来的复杂性,这似乎是合理的。因此,此设计决策的原因不一定是可能的安全问题(正如问题所暗示的那样)。

即将发表的OOPSLA论文中,有关泛型的交集类型和限制的更多讨论。


答案 2

禁止这样做的两个可能原因:

  1. 复杂性。JDK-4899305 表明,包含类型参数和其他参数化类型的边界将允许比现有类型更复杂的相互递归类型。简而言之,布鲁诺的答案

  2. 指定非法类型的可能性。具体来说,使用不同的参数将泛型接口扩展两次。我无法想出一个非人为的例子,但是:

    /** Contains a Comparator<String> that also implements the given type T. */
    class StringComparatorHolder<T, C extends T & Comparator<String>> {
      private final C comparator;
      // ...
    }
     
    void foo(StringComparatorHolder<Comparator<Integer>, ?> holder) { ... }

现在是一个和一个.我不清楚这会给编译器带来多大的麻烦,但这显然不好。特别假设它有一个这样的方法:holder.comparatorComparator<Integer>Comparator<String>Comparator

void sort(List<? extends T> list);

我们的/混合现在有两种具有相同擦除的方法:Comparator<Integer>Comparator<String>

void sort(List<? extends Integer> list);
void sort(List<? extends String> list);

正是由于以下原因,您无法直接指定此类类型:

<T extends Comparator<Integer> & Comparator<String>> void bar() { ... }
java.util.Comparator cannot be inherited with different arguments:
    <java.lang.Integer> and <java.lang.String>

由于允许您间接地做同样的事情,因此它也出来了。<A extends I & Adapter<E>>


推荐