Java 为什么可以';我在具有多个边界的类型参数中使用类型参数吗?

Java 为什么可以';我在具有多个边界的类型参数中使用类型参数吗?,java,generics,constraints,Java,Generics,Constraints,所以,我知道下面的方法行不通,但为什么行不通呢 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,

所以,我知道下面的方法行不通,但为什么行不通呢

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));
    }
}
接口适配器{}
类适配器{
void add(类extl,类intl){
addAdapterFactory(新AdapterFactory(extl,intl));
}
}
add()
方法给了我一个编译错误,“当第一个绑定是类型参数时无法指定任何额外的绑定适配器”(在Eclipse中),或者“类型参数后面不能跟其他绑定”(在IDEA中),请选择

很明显,在
&
之前,不允许使用类型参数
I
,仅此而已。(在你问之前,如果你切换它们是不行的,因为不能保证
I
不是一个具体的类。)但为什么不呢?我看过安吉丽卡·兰格的常见问题,但找不到答案

通常,当某些泛型限制看起来是任意的时,这是因为您创建了一种类型系统无法实际执行正确性的情况。但我不知道什么样的案例会破坏我在这里所做的。我想说,它可能与类型擦除后的方法分派有关,但只有一个
add()
方法,所以它不像有任何歧义


有人能给我演示一下这个问题吗?

这可能并不能回答根本问题,但我只想指出,规范明确禁止这样做。谷歌搜索错误消息将我带到,这进一步指向:

绑定由一个类型变量或一个类或接口类型T组成,后面可能跟有更多的接口类型I1,…,In

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


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

我也不知道为什么会有限制。您可以尝试向Java5泛型的设计者(主要是GiladBracha和NealGafter)发送一封友好的电子邮件

我的猜测是,他们只想支持绝对最小值(这就是多个边界的本质),以使语言不比需要的复杂。交叉点不能用作类型注释;程序员只能在交叉点显示为类型变量的上限时表示交叉点

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

public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll)
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
max
的历史签名不匹配,并导致旧客户端中断。 对于多个边界,擦除只考虑最左边的边界,因此如果
max
被赋予以下签名:

public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll)
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
它等于泛型之前的
max
签名

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

更多关于交叉点类型和泛型限制的讨论。

以下是另一段引用:

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


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

将其定为非法的两个可能原因:

  • 复杂性。建议包含类型参数和其他参数化类型的绑定将允许比现有类型更复杂的相互递归类型。简言之

  • 指定非法类型的可能性。明确地我想不出一个非人为的例子,但是:

    /** 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) { ... }
    我们的
    比较器
    /
    比较器
    混合现在有两种相同的擦除方法:void sort(列表我遇到了同样的问题,并找到了一个有效的解决方案:

    void sort(List<? extends Integer> list); void sort(List<? extends String> list);
        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;
            }
        }
    
    接口适配器
    {}
    接口适配器
    {
    
    void add(container因为我可能是一个类?仅仅因为我是一个extends,并不意味着我是一个接口(好的,这将成为一个接口),但A可能很容易成为I的子类,这是规范禁止的。如果I是类,会有什么问题?适配器是一个接口。事实上,如果多个边界的点是控制擦除,这完全是有意义的,因为我只是要擦除到对象。有趣。必须仔细考虑一下这个问题。看起来仍然像t无论如何,在foo()的声明中,他会抓住它。但也许有一个更人为的例子可以让它更清楚。:)James Iry说它在Scala中有效:@Pacerier,谢谢。不幸的是,我现在似乎找不到它的实时副本:(也许他们也不知道?这句话实际上似乎鼓励在类型交集中使用类型变量,而不是禁止它。我认为这篇文档有很大的误导性。 <T extends Comparator<Integer> & Comparator<String>> void bar() { ... } java.util.Comparator cannot be inherited with different arguments: <java.lang.Integer> and <java.lang.String>
        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;
            }
        }