我想知道为什么这段代码编译成功?
源代码:
abstract class A<K extends Number>
{
public abstract <M> A<? super M> useMe(A<? super M> k);
}
已成功编译
它是如何工作的以及为什么编译? M是任何类型,为什么它可以使用?应该是:<M extends Number>
?
这不会编译:
abstract class A<K extends Number>
{
public abstract <M> A<? super M> useMe(A<M> k);
}
错误讯息:
类型参数M不在类型变量K的范围内,其中M,K是类型变量: M扩展方法useMe(A)中声明的Object K扩展了在A类中声明的数字
有什么区别?
答案 0 :(得分:5)
this Eclipse bug讨论了这种编译器行为。最初,Eclipse编译器在您的示例中为表达式执行了错误,而javac则没有。虽然我还没有直接搜索JLS,但是共识似乎是规范中没有任何内容要求根据类型参数边界检查较低的有界通配符。在这种情况下,它最终留给调用者分配一种满足约束的类型(正如Stephan Herrmann在该帖子中推测的那样)。
答案 1 :(得分:2)
这是一段令人惊讶的无意义的代码。
所有这一切都是说,A
类采用K
的通用类型Number
,并且有一个方法useMe
返回A<T>
对T
有一些无意义的额外限制(显然不是Number
)。
这是一个实施,以显示糖的说法有多少:
abstract class A<K extends Number> {
public abstract <M> A<? super M> useMe(A<? super M> k);
}
class B extends A<Number> {
@Override
public <M> A<? super M> useMe(A<? super M> k) {
// Not much more you can do here but this.
return k;
}
}
? super M
的东西只是毫无意义的gobbledegook - 所有编译器都可以从中派生出来的是传递给它的参数和返回的结果必须是特定未命名类的超类。
泛型可以在编译时轻松检测编码错误。使用像这样的mumbo-jumbo只是误导混淆。
答案 2 :(得分:1)
向第一个示例添加<M extends Number>
不会添加编译器关心的任何内容。请记住,如果我们说“M是数字的子类型”和“类型是M的超类型”,你说的是“M的超类型”,我们实际上并没有说这种类型是否为子类型号。
有关更好的示例,请M
为Integer
,变量属于A<Object>
类型。虽然显然不起作用,但它能正确满足功能的所有要求。
由于无法修复函数定义,它只是让它通过并假设调用站点将捕获问题。
答案 3 :(得分:-1)
您的问题分为两部分:
第1部分:什么是<M>
?
方法上存在泛型参数使其成为“类型化方法”,这意味着该方法具有由调用者确定的泛型类型,通常通过推理来确定。它可能是有界的。如果类也有类型,并且方法是实例方法,则这两种类型是不相关的。
第2部分:
通用类型必须与完全匹配。原因归结为如果B
是A
的子类型,SomeClass<T extends B>
不是SomeClass<T extends A>
的子类型,更具体地说,SomeClass<A>
不是SomeClass<? super A>
的子类型子类型{{1}}。