2008-10-13 55 views
43

所以,我明白以下不起作用,但是为什么不起作用?为什么我不能在具有多个边界的类型参数中使用类型参数?

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

add()方法给我一个编译错误,“不能指定任何附加绑定适配器<Ë>当第一结合是一种类型的参数”(在Eclipse),或“类型参数不能被随后的其他边界” (在IDEA中),请选择。

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

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

有人可以证明我的问题吗?

回答

23

我也不确定为什么限制在那里。您可以尝试发送一封友好的电子邮件给Java 5 Generics(主要是Gilad Bracha和Neal Gafter)的设计人员。

我的猜测是他们只想支持绝对最小值intersection types(这是多边界本质上是什么),使语言不再比需要更复杂。交集不能用作类型注释;一个程序员只能表达一个交点,当它出现在一个类型变量的上界时。

为什么这种情况下甚至支持?答案是多重边界允许您控制擦除,这可以在生成现有类时保持二进制兼容性。正如Naftalin和Wadler在book第17.4解释,一个max方法将逻辑上具有以下特征:

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

然而,这将删除到:

public static Comparable max(Collection coll) 

不匹配的历史签名max,并导致老客户中断。 随着多重限制,只有最左边的结合被认为是擦除,因此,如果max给出以下特征:

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

然后其签名的擦除变为:

public static Object max(Collection coll) 

哪个等于泛型之前的max的签名。

这似乎也合情合理Java的设计者只关心这个简单的例子,并限制其他(更先进的)使用交集类型的,因为他们只是不确定的复杂性,它可能带来的。所以这个设计决定的原因并不需要是一个可能的安全问题(如问题所示)。

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

+0

实际上,如果多个边界点是要控制擦除,那完全有意义,因为我只是要擦除Object。 – 2008-10-13 13:11:46

1

这可能不回答根本问题,但只是想指出规范明确禁止它。谷歌搜索的错误消息,带我去this blog entry,这进一步指向jls 4.4

结合的方案包括将类型变量,或者一个类或接口类型T接着可能进一步接口类型I1的,... ,在。

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

为什么限制?我不知道。

+0

因为我可能是一类?仅仅因为一个扩展我并不意味着我是一个接口(好吧,这将使一个接口),但是A可以很容易地的我,这是由规范 – 2008-10-13 11:50:44

+0

禁止的是什么问题,如果我是一个类的子类?已知适配器是一个接口。 – 2008-10-13 12:21:48

9

下面是从JLS另一个报价:

绑定的形式被限制(仅第一元件可以是一个类或类型的变量,和仅一种类型的可变可能出现在约束)预先排除某些尴尬的情况出现

究竟是那些尴尬的情况,我不知道。为取缔这种

+1

也许他们也不知道? – Pacerier 2014-08-25 23:07:52

12

两个可能的原因:

  1. 复杂性。 Sun bug 4899305表明,一个含键合的类型参数加上附加的参数化的类型将允许甚至更复杂的相互递归类型比已经存在。总之,Bruno's answer

  2. 指定非法类型的可能性。具体而言,extending a generic interface twice with different parameters。我不能拿出一个非人为的例子,但:

    /** 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>>让你做同样的事情,间接地,它出来了。

相关问题