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设计人员似乎只关心这种简单的情况,并限制交叉点类型的其他(更高级)使用,因为他们不确定它可能带来的复杂性。因此,该设计决策的原因不一定是一个可能的安全问题(如问题所示)
更多关于交叉点类型和泛型限制的讨论。以下是另一段引用:
限制边界的形式(只有第一个元素可以是类或类型变量,并且只有一个类型变量可以出现在边界中)以避免出现某些尴尬的情况
我不知道这些尴尬的情况到底是什么。将其定为非法的两个可能原因:
我们的
比较器
/比较器
混合现在有两种相同的擦除方法: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;
}
}