您如何进行这项工作:
public class Frankenstein<T extends IHuman, IMonster>{
}
不做
public interface Weirdo extends Ihuman, IMonster{
}
编辑
为什么这不起作用?
public <T> void mapThis(
Class<? extends MyClass<T>> key, Class<? extends T & IDisposable> value) {
}
我正在将编译器消息标记Class<? extends T & IDisposable>
为错误。
Reimeus已经指出,您在编辑中要求的内容是不可能的。我只想扩展一下原因。
人们会认为您可以使用以下内容:
public <T, U extends T & IDisposable> void mapThis(
Class<? extends MyClass<T>> key,
Class<? extends U> value
) { ... }
实际上,这就是我第一次看到这篇文章时想到的。但这实际上会导致编译器错误:
类型变量不能跟其他界限
为了帮助我解释原因,我想引用Victor Rudometov在OracleBlogs上发表的有关此错误的文章:
这个事实并不总是很清楚,但确实如此。以下代码不应编译:
interface I {}
class TestBounds <U, T extends U & I> {
}
因为JLS第4章类型,值和变量部分4.4类型变量规定:“ 结合的由 任一 类型变量,或者可能接着进一步接口类型的类或接口类型T I
1,…,I Ñ。
” 。因此,可以使用 T扩展U,T扩展SomeClass&I ,但不可以 T扩展U&I
。该规则适用于所有情况,包括类型变量以及方法和构造函数中的界限。
总而言之,施加此限制是为了“避免某些尴尬的情况出现”(JLS§4.9)。
什么样的尴尬情况?克里斯·波维尔克(ChrisPovirk)的回答描述了一个:
[限制的原因是]指定非法类型的可能性。具体而言,使用不同的参数两次扩展通用接口。我无法提出一个非人为的示例,但是:
/** Contains a Comparator<String> that also implements the given type T.
*/
class StringComparatorHolder> {
private final C comparator;
// …
}void foo(StringComparatorHolder<Comparator<Integer>, ?> holder) { ... }
现在
holder.comparator
是Comparator<Integer>
和Comparator<String>
。
克里斯还指出了Sun bug4899305,它是与该语言限制相抗衡的bug。由于无法修复,已关闭,并带有以下注释:
如果类型变量后面可以跟着类型变量或(可能是参数化的)接口,则可能会有更多相互递归的类型变量,这很难处理。当边界仅仅是参数化类型(例如)时,事情已经很复杂
<S,RextendsComparable<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, 是 一个用例你想做什么。由于编译器如何推断通用方法类型参数的限制,我的上述推理是不合时宜的。采用以下通用方法:
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指出了这一点)。
问题内容: 我正在尝试重构M类型确实扩展了任何内容的类和子类集,即使我们知道它必须是某种类型的子类也是如此。该类型已参数化,我希望其参数化类型可用于已经具有M值的子类。 有什么方法可以定义此类,而不必在参数列表中包括冗余的K和V泛型类型。我希望能够使编译器从子类映射到的M中推断出它们。 换句话说,我希望类声明看起来像这样: 从M的定义可以推断出K和V的类型。 问题答案: 问题在于,它们并没有真正地
问题内容: 我收到一个错误,程序中有这个结构 错误指向Circle扩展Shapes < T >类,其中指出“ T无法解析为类型”。如果将T设置为字符串,错误将消失,但这也意味着我只能使用一种数据类型。我应该在<>中放入什么,以便可以使用任何数据类型(字符串,整数,双精度型等),或者这样做是错误的? 问题答案: 有两个不同的概念。当你写 这意味着您正在创建一个类,该类在实例化时将被某个类参数化。您不
问题内容: 当我以为自己终于了解了泛型时,我遇到了以下示例: 它说,如果第1行未注释,则不会编译以下内容: 如果注释了第2行,则不会编译以下内容: 在第二个示例中,允许使用脂肪族及其超类型。那么为什么不允许脂肪族? 在第一个示例中,为什么不允许? 第一个编译器错误: 第二编译器错误: 问题答案: 您的第一个声明 表示 可能 是一个(因为extends ,extends 和extended )。 在
问题内容: 我不明白为什么要编译。我以为它检查了但没有检查? 问题答案: 泛型中的关键字在语义上与常规关键字略有不同。 当使用在泛型的上下文中,例如,这意味着,应该是一个类型的 任一 实现的接口(在情况下,当是接口), 或者 是子类(如果是一个类)。 可能的原因是,如果Generics中支持该关键字,则会使类型参数声明过于冗长。 例如,您将拥有: 相反,此方法的有效语法为: 实际上,您不需要关键字
问题内容: 我想知道是否有可能编写一个接受多种通用类型的函数,如下所示: 那行得通吗?每个参数中的泛型是否意味着每个参数必须具有与泛型相同的类型T? 问题答案: 是的-可能的(虽然不是使用方法签名),是的,使用签名的类型必须相同。 使用给定的签名,必须在呼叫站点将其与单个类型(例如 或 )相关联。但是,您可以声明采用多个类型参数的方法签名 请注意,在上面的签名中,我已经声明了类型以及签名本身。因此
问题内容: 我有一个代表文本片段的泛型类。该文本片段可能具有多种不同模式(突出显示的不同类型)中的任何一种。这些模式用枚举表示。每个项目的Enum可能不同,但是它必须实现一个接口,该接口提供了一种将其中两个结合的方法(可以突出显示并加粗显示)。所以我有一个界面: 然后我的TextFragment是文本字符串和模式的容器。但是当我尝试声明该类时: 我收到以下错误: 令牌“扩展”的语法错误,预期 根据