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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/197190/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:23:06  来源:igfitidea点击:

Why can't I use a type argument in a type parameter with multiple bounds?

javagenericsconstraints

提问by David Moles

So, I understand thatthe following doesn't work, but whydoesn't it work?

所以,我的理解下列哪项不工作,但为什么不工作?

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));
    }
}

The add()method gives me a compile error, "Cannot specify any additional bound Adapter<E> when first bound is a type parameter" (in Eclipse), or "Type parameter cannot be followed by other bounds" (in IDEA), take your pick.

add()方法给了我一个编译错误,“当第一个绑定是一个类型参数时,不能指定任何额外的绑定 Adapter<E>”(在 Eclipse 中),或者“类型参数不能跟其他边界”(在 IDEA 中),请选择.

Clearly you're just Not Allowed to use the type parameter Ithere, before the &, and that's that. (And before you ask, it doesn't work if you switch 'em, because there's no guarantee that Iisn't a concrete class.) But why not? I've looked through Angelika Langer's FAQ and can't find an answer.

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

Generally when some generics limitation seems arbitrary, it's because you've created a situation where the type system can't actually enforce correctness. But I don't see what case would break what I'm trying to do here. I'd say maybe it has something to do with method dispatch after type erasure, but there's only one add()method, so it's not like there's any ambiguity...

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

Can someone demonstrate the problem for me?

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

采纳答案by Bruno De Fraine

I'm also not sure why the restriction is there. You could try sending a friendly e-mail to the designers of Java 5 Generics (chiefly Gilad Bracha and Neal Gafter).

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

My guess is that they wanted to support only an absolute minimum of intersection types(which is what multiple bounds essentially are), to make the language no more complex than needed. An intersection cannot be used as a type annotation; a programmer can only express an intersection when it appears as the upper bound of a type variable.

我的猜测是,他们只想支持绝对最小的交集类型(这就是多重​​边界本质上的含义),以使语言不会比需要的更复杂。交集不能用作类型注释;程序员只有当它出现在类型变量的上界时才能表达交集。

And why was this case even supported? The answer is that multiple bounds allow you to control the erasure, which allows to maintain binary compatibility when generifying existing classes. As explained in section 17.4 of the bookby Naftalin and Wadler, a maxmethod would logically have the following signature:

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

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

However, this erases to:

但是,这会擦除:

public static Comparable max(Collection coll)

Which does not match the historical signature of max, and causes old clients to break. With multiple bounds, only the left-most bound is considered for the erasure, so if maxis given the following signature:

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

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

Then the erasure of its signature becomes:

然后其签名的擦除变为:

public static Object max(Collection coll)

Which is equal to the signature of maxbefore Generics.

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

It seems plausible that the Java designers only cared about this simple case and restricted other (more advanced) uses of intersection types because they were just unsure of the complexity that it might bring. So the reason for this design decision does not need to be a possible safety problem (as the question suggests).

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

More discussion on intersection types and restrictions of generics in an upcoming OOPSLA paper.

即将发布的 OOPSLA 论文中更多地讨论了泛型的交叉类型和限制。

回答by Chris Povirk

Two possible reasons for outlawing this:

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

  1. Complexity. Sun bug 4899305suggests that a bound containing a type parameter plus additional parameterized types would allow for even more complicated mutually recursive types than already exist. In short, Bruno's answer.

  2. The possibility of specifying illegal types. Specifically, extending a generic interface twice with different parameters. I can't come up with a non-contrived example, but:

    /** 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) { ... }

  1. 复杂。 Sun 错误 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) { ... }

Now holder.comparatoris a Comparator<Integer>and a Comparator<String>. It's not clear to me exactly how much trouble this would cause for the compiler, but it's clearly not good. Suppose in particular that Comparatorhad a method like this:

现在holder.comparator是 aComparator<Integer>和 a Comparator<String>。我不清楚这到底会给编译器带来多少麻烦,但这显然不好。特别假设Comparator有这样的方法:

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

Our Comparator<Integer>/ Comparator<String>hybrid now has two methods with the same erasure:

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

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

It's for these kinds of reasons that you can't specify such a type directly:

由于这些原因,您不能直接指定这样的类型:

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

Since <A extends I & Adapter<E>>allows you to do the same thing indirectly, it's out, too.

既然<A extends I & Adapter<E>>允许你间接地做同样的事情,它也出来了。

回答by Jan Soltis

Here's another quote from JLS:

这是JLS的另一句话:

The form of a bound is restricted (only the first element may be a class or type variable, and only one type variable may appear in the bound) to preclude certain awkward situations coming into existence.

边界的形式受到限制(只有第一个元素可以是类或类型变量,并且边界中只能出现一个类型变量)以防止出现某些尴尬的情况

What exactly are those awkward situations, I don't know.

那些尴尬的情况到底是什么,我不知道。

回答by Miserable Variable

This probably does not answer the root question, but just want to point out that the spec unambiguously forbids it. Google search for the error message took me to this blog entry, which further points to jls 4.4:

这可能不能回答根本问题,而只是想指出规范明确禁止它。谷歌搜索错误消息把我带到这个博客条目,它进一步指向jls 4.4

The bound consists of either a type variable, or a class or interface type T possibly followed by further interface types I1 , ..., In.

边界由类型变量或类或接口类型 T 组成,可能后跟进一步的接口类型 I1 , ..., In 。

So, if you use type parameter as bound you cannot use any other bound, just as the error message says.

因此,如果您使用类型参数作为绑定,则不能使用任何其他绑定,正如错误消息所述。

Why the restriction? I have no idea.

为什么要限制?我不知道。

回答by Ealrann

I had the same problem, and found a working solution:

我遇到了同样的问题,并找到了一个可行的解决方案:

    interface Adapter<E>
    {}

    interface Adaptulator<I>
    {
        void add(Container<?, ? extends I> container);
    }

    static final class Container<E, I extends Adapter<E>>
    {
        public final Class<E> extl;
        public final Class<I> intl;

        public Container(Class<E> extl, Class<I> intl)
        {
            this.extl = extl;
            this.intl = intl;
        }
    }

Why it's working

为什么它有效

To understand that, we need to state our requirements:

为了理解这一点,我们需要说明我们的要求:

  1. Keep two different generics synchronized on something. In your case, this is the E.
  2. One of the two generic needs to have some extra inheritance, here it's I.
  1. 在某事上保持两个不同的泛型同步。在您的情况下,这是E
  2. 两个泛型之一需要有一些额外的继承,这里是I

Creating an extra class allow to meet these two requirements, by creating a tight context.

通过创建一个紧密的上下文,创建一个额外的类可以满足这两个要求。

  1. An extra requirement (but probably the most important) was the need of a generic method (not bound too much to our class).
  1. 一个额外的要求(但可能是最重要的)是需要一个泛型方法(不要过多地绑定到我们的类)。

This is solved by the permissive parameter Container<?, ? extends I>.

这是由 permissive 参数解决的Container<?, ? extends I>

Note

笔记

It's just a guess, but in this kind of usage, in general, you quickly need a ? super Aor ? super Isomewhere.

这只是一个猜测,但在这种用法中,一般来说,您很快就需要一个? super A? super I某个地方。