请比较两个代码段:
snippet1:
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Child<String> p =new Child<String>();
p.m("1");
}
}
class Parent <T>{
void m(T t){
}
}
class Child<T extends CharSequence> extends Parent<String>{
void m(T t){
}
}
结果(online compiler):
Main.java:13:错误:对m的引用是不明确的,两个方法都是m(T#1) in Parent和方法m(T#2)in Child match p.m(“1”); ^哪里 T#1,T#2是类型变量: T#1扩展了在Parent类中声明的Object T#2扩展了在Child类中声明的CharSequence
snippet2(只有一次更改!!!):
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Parent<String> p =new Child<String>();
p.m("1");
}
}
class Parent <T>{
void m(T t){
}
}
class Child<T extends CharSequence> extends Parent<String>{
void m(T t){
}
}
这段代码汇编得很好!(online compiler)
请澄清这种不同。
答案 0 :(得分:3)
所以事情就是在这里,你实际上所发生的事情并非重写。请注意,如果您在@Override
中将m()
注释添加到Child
,则在以下两种情况下都会出现编译错误:
Main.java:22: error: method does not override or implement a method from a supertype
@Override
你可以想象Child
实际上是这样的:
class Child<T extends CharSequence> extends Parent<String>{
void m(T t){
}
void m(String t) { // <-- This was the method inherited from Parent
}
}
所以当你尝试
时Child<String> p =new Child<String>();
编译器看到引用类型Child<String>
并看到您实际上有两个方法可以正常工作,因为您拥有从{{1}继承的m(String t)
}和Parent<String>
在m(T t)
中定义,其中Child
。因为现在你实际上有两个T == String
方法,你会有一个模糊的调用。
现在,如果你有
m(String t)
编译器看到引用类型Parent<String> p =new Child<String>();
并使用它来解析对Parent<String>
的调用。由于m(String)
只定义了一个这样的方法,所以没有模糊的方法调用,所以代码编译。
这是Parent<String>
注释应始终使用的原因之一 - 对于您是重载方法还是重写方法没有疑惑。
有趣的事实:如果内存服务,这实际上是少数几次 如果您反编译@Override
,您会看到标题
Child
答案 1 :(得分:2)
问题是您实际上没有覆盖m(T)
的{{1}}方法。因此,您有两个独立的功能:
Parent
,因为您强制父级中的T为String Parent.m(String s)
想象一下Child的这个声明:
Child.m(T)
现在,class Child<T extends CharSequence> extends Parent<Integer>
变为Parent<T>
,这意味着Parent<Integer>
的{{1}}方法,现在是m
方法,而你有在您的子课程中,仍然可以使用Parent
方法。现在区别很明显。
您选择m(Integer i)
为m(T)
,巧合地扩展了T
,但这并未使其覆盖Parent的String
方法。因此,将您的签名更改为:
CharSequence
应该有效。现在你真的重写了Child in Parent的方法。
答案 2 :(得分:2)
你的Child
类通过声明一个依赖于有界类型的参数的方法来绕过覆盖规则(并进入重载领域)。该类型可能是String
,也可能不是,因此在编译两者时,父类中声明的方法不会发生冲突。
但是,在编译Ideone
类时,通过在
String
Child<String> p = new Child<String>();
类型String
绑定到T
Child
声明并在Child#m(..)
中使用的类型参数void m(String t) {}
。因此,该方法显示为
Parent#m(..)
但String
也是如此,因为Child
类声明中的类型参数class Child<T extends CharSequence> extends Parent<String> {
。
Child
因此,m(String)
类有两个Parent<String> p =new Child<String>();
p.m("1");
方法用于该调用。这个电话很模糊。
在
Child
由于您的引用属于Parent
类型,因此{{1}}类方法不可见。没有含糊之处。