Reimeus已经指出,您在编辑中要求的内容是不可能的。 我只想扩展一下原因。
有人会认为您可以使用以下内容:
public void mapThis(
Class extends MyClass> key,
Class extends U> value
) { ... }
实际上,这就是我第一次看到这篇文章时想到的。 但这实际上会导致编译器错误:
类型变量不能跟其他界限
为了帮助我解释原因,我想引用Victor Rudometov的Oracle Blogs帖子中的错误提示:
这个事实并不总是很清楚,但确实如此。 以下代码 不应编译:
>
>
>
因为JLS第4章 “类型,值和变量”部分4.4类型变量指出:“ bound由类型变量或类或接口类型组成 T可能后面跟着其他接口类型I1,...,In。”。 可以使用T扩展U,T扩展SomeClass&I,但不可以T扩展U&I。 此规则适用于所有情况,包括类型变量和 方法和构造函数。
在一个密切相关的文章中探讨了这种限制的原因:为什么我不能在具有多个界限的类型参数中使用类型参数?
总而言之,施加此限制是为了“避免某些尴尬的情况出现”(JLS§4.9)。
什么样的尴尬情况? 克里斯·波维尔克(Chris Povirk)的回答描述了一个:
[限制的原因是]指定非法类型的可能性。 具体来说,使用不同的参数两次扩展通用接口。 我无法提出一个非人为的示例,但是:
>
现在>是S&T和S&Comparable。
克里斯还指出了Sun bug 4899305,它是与该语言限制相抗衡的bug。 由于无法修复,已关闭,并带有以下注释:
如果类型变量后面可以跟类型变量或(可能 参数化)接口,则相互之间可能会更多 递归类型变量,很难处理。 东西 当边界仅仅是参数化类型时,已经很复杂了, 例如 >.因此,界限不会继续 现在改变。 javac和Eclipse都同意S&T和 S&Comparable是非法的。
因此,这就是限制的背后原因。 我要特别指出泛型方法(您的问题所涉及的问题),我还要进一步指出,类型推断理论上将导致这种界限毫无意义。
如果我们重新检查以上假设签名中声明的类型参数:
假设呼叫者未明确指定MyClass.foo("asdf", 42)和Object,可以将其简化为以下内容:
或者只是这个(细微的差别,但这是另一个主题):
这是因为MyClass.foo("asdf", 42)没有任何界限,所以无论传入哪种类型的参数,Object都可以始终至少解析为mapThis,然后U也可以。
让我们回过头说MyClass.foo("asdf", 42)是有界的:
可以用相同的方式减少它(MyClass.foo("asdf", 42)可以是类或接口):
基于这种推理,就将调用方限制为更特定的参数而言,您要尝试实现的语法毫无意义。
Java 8之前的附录:
在Java 8之前,您正在尝试使用一个用例。 由于编译器如何推断通用方法类型参数的限制,我的上述推理是不合时宜的。 采用以下通用方法:
class MyClass {
static void foo(T t1, T t2) { }
}
这是尝试制作一个采用两个“相同类型”参数的方法的常见初学者错误。 当然,由于继承的方式,这毫无意义:
MyClass.foo("asdf", 42); // legal
在这里,MyClass.foo("asdf", 42)推断为Object-这与简化mapThis类型参数的先前推理相吻合。 您必须手动指定类型参数以实现预期的类型检查:
MyClass.foo("asdf", 42); // compiler error
但是,这是您的用例开始出现的地方,带有交错边界的多个类型参数是另一回事:
class MyClass {
static void foo(T t, U u) { }
}
现在,此调用错误:
MyClass.foo("asdf", 42); // compiler error
表已经翻转了-我们必须手动放松类型参数才能进行编译:
MyClass.foo("asdf", 42); // legal
发生这种情况是因为编译器推断方法类型参数的方式有限。 因此,您想要实现的目标实际上是在限制调用者参数的应用程序。
但是,此问题似乎已在Java 8中解决,并且MyClass.foo("asdf", 42)现在可以编译而没有任何错误(感谢Regent指出了这一点)。