java泛型继承。超载或覆盖

时间:2014-06-13 22:34:51

标签: java generics overloading override type-erasure

请比较两个代码段:

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

请澄清这种不同。

3 个答案:

答案 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}}类方法不可见。没有含糊之处。