Reimeus 已经指出,您在编辑中要求的内容是不可能的。我只是想稍微解释一下为什么。
有人会认为您可以使用以下内容:
public <T, U extends T & IDisposable> void mapThis(
Class<? extends MyClass<T>> key,
Class<? extends U> value
) { ... }
事实上,这就是我第一次看到这篇文章时想到的。但这实际上给出了编译器错误:
类型变量后面不能有其他界限
为了帮助我解释原因,我想引用 Victor Rudometov 关于此错误的Oracle 博客文章:
这个事实并不总是很清楚,但确实如此。以下代码不应编译:
interface I {}
class TestBounds <U, T extends U & I> {
}
因为 JLS 第 4 章类型、值和变量第 4.4 节类型变量指出:“边界由类型变量或类或接口类型 T 可能后跟进一步的接口类型 I 1,...,I n组成。 ” . 所以可以使用T extends U, T extends SomeClass & I,但不能使用T extends U & I。此规则适用于所有情况,包括方法和构造函数中的类型变量和边界。
这种限制的原因在一篇密切相关的文章中进行了探讨:为什么我不能在具有多个边界的类型参数中使用类型参数?
总而言之,施加限制是为了“排除某些尴尬情况的出现”(JLS §4.9)。
有哪些尴尬的情况?Chris Povirk 的回答描述了一个:
[限制的原因是] 指定非法类型的可能性。具体来说,使用不同的参数两次扩展通用接口。我想不出一个非人为的例子,但是:
/** 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.comparator
是 aComparator<Integer>
和 a Comparator<String>
。
Chris 还指出了Sun 错误 4899305,这是一个与此语言限制有争议的错误。它因无法修复而关闭,并附有以下评论:
如果类型变量后面可以跟类型变量或(可能是参数化的)接口,则可能会有更多相互递归的类型变量,这很难处理。当绑定只是一个参数化类型时,事情已经很复杂了,例如<S,R extends Comparable<S>>
. 因此,界限现在不会改变。javac 和 Eclipse 都同意这一点S&T
并且
S&Comparable<S>
是非法的。
这就是限制背后的原因。具体解决泛型方法(您的问题涉及),我想进一步指出,类型推断理论上会导致这样的界限无论如何都是毫无意义的。
如果我们重新检查上面假设签名中声明的类型参数:
<T, U extends T & IDisposable>
假设调用者没有明确指定T
and U
,这可以简化为以下内容:
<T, U extends Object & IDisposable>
或者只是这个(细微的区别,但这是另一个话题):
<T, U extends IDisposable>
这是因为T
没有任何界限,所以无论传入什么类型的参数,至少T
总是可以解析为,然后可以。Object
U
让我们回过头来说T
是有界的:
<T extends Foo, U extends T & IDisposable>
这可以以相同的方式减少(Foo
可以是类或接口):
<T extends Foo, U extends Foo & IDisposable>
基于这种推理,就将调用者限制为更具体的参数而言,您尝试实现的语法毫无意义。
Java 8 之前的附录:
在 Java 8 之前,您正在尝试做的事情有一个用例。由于编译器如何推断泛型方法类型参数的限制,我的上述推理不再适用。采用以下通用方法:
class MyClass {
static <T> void foo(T t1, T t2) { }
}
这是一个常见的初学者错误,试图创建一个采用“相同类型”的两个参数的方法。当然,由于继承的工作方式,这毫无意义:
MyClass.foo("asdf", 42); // legal
在这里,T
推断为Object
- 这与先前关于简化mapThis
类型参数的推理相匹配。您必须手动指定类型参数才能实现预期的类型检查:
MyClass.<String>foo("asdf", 42); // compiler error
但是,这是您的用例开始出现的地方,对于具有交错边界的多个类型参数,情况就不同了:
class MyClass {
static <T, U extends T> void foo(T t, U u) { }
}
现在这个调用错误:
MyClass.foo("asdf", 42); // compiler error
情况发生了变化——我们必须手动放宽类型参数以使其编译:
MyClass.<Object, Object>foo("asdf", 42); // legal
发生这种情况是因为编译器推断方法类型参数的方式有限。出于这个原因,您想要实现的实际上将有一个应用程序来限制调用者的参数。
然而,这个问题似乎已经在 Java 8 中得到修复,MyClass.foo("asdf", 42)
现在编译没有任何错误(感谢 Regent 指出这一点)。