【问题标题】:inheritance in java and generics [duplicate]java和泛型中的继承[重复]
【发布时间】:2016-08-16 17:19:18
【问题描述】:

我同意在不扩展 B 类的情况下,输出为“1233”,但为什么如果用 A 扩展 B 类,则代码无法编译?

public class SomeClass {

    public static class A {

        public void f(int x){
            System.out.print("1");
        }

        public void f(Object x){
            System.out.print("2");
        }
    }

    public static class B // extends A {

        public <T> void f(T x){
             System.out.print("3");
        }
    }

    public static void main( String[] args){
        A a=new A();
        B b=new B();
        a.f(3);
        a.f("hello");
        b.f(3);
        b.f("hello");
    }
}

【问题讨论】:

  • 错误信息告诉你什么?

标签: java generics inheritance


【解决方案1】:

方法签名导致名称冲突,以下两个方法签名存在名称冲突问题,因为泛型方法实际上并没有覆盖前一个。在type erasure 之后,两者将具有相同的签名。

public <T> void f(T x){...}
public void f(Object x){...}

B 中声明的方法没有覆盖A 的方法,因为如果说该方法被覆盖,

JLS 8.4.8.1. Overriding (by Instance Methods)

在类 C 中声明或继承的实例方法 mC,覆盖 来自 C 类 A 中声明的另一个方法 mA,当且仅当满足以下所有条件 是真的:

  • A 是 C 的超类。
  • C 不继承 mA
  • mC 的签名是 mA 签名的子签名 (§8.4.2)。
    ....

以下是有效的并且可以正常编译,

public static class A {

    public <T> void f(T x) {
        System.out.print("3");
    }
}

public static class B extends A {

    @Override
    public void f(Object x) {
        System.out.print("2");
    }

}

【讨论】:

  • 这样说对吗:方法不能被泛型方法覆盖(换句话说,父类可以是泛型的,但不能是后代的)?
  • @tami 不完全是。与基本方法相同的签名也有效,其他一些签名在泛型方法的情况下也有效。
  • "Same signature as the base method is also valid" ,是否意味着在两个类(A和B)中都写“public void f(T x)”是正确的?如果我输入“public void f(int x)”(不仅是“public void f(Object x)”),它是否也是一种覆盖?
【解决方案2】:

Java 泛型使用类型擦除。 javac在解决时会发生冲突

public <T> void f(T x);
public void f(Object x);

两者在编译器javac 方面是相同的。

添加另一个example

class Test{
   void add(Set<Integer> ii){}
   void add(Set<String> ss){}
}

This limitation is part of the language syntax, not the Java runtime itself. Essentially, this rule is intended to avoid conflicts in legacy code that still uses raw types.

请参考上面的答案。

【讨论】:

    【解决方案3】:

    由于类型擦除问题,您无法编译上述类。

    您在基类中拥有的方法是通用的,并且与父类中的f(Object x) 匹配。这将在编译时发生。

    public void f(Object x){}
    public <T> void f(T x){}
    

    请参阅this Oracle documentation 了解类型擦除。


    但是,你可以保留int 方法,它会编译得很好:

    class A {
          public void f(int x){
             System.out.print("1");
          }
    }
    
    public class B extends A
    {
          public <T> void f(T x){
             System.out.print("3");
          }
    }
    

    【讨论】:

    • 这不是真的。去掉f(int x)方法还是编译不出来。
    【解决方案4】:

    只有当参数列表与被覆盖方法的参数列表完全相同并且返回类型应该相同或被覆盖方法的返回类型(在父类中)的子类型时,才能在派生类中覆盖方法。

    您可以覆盖以下方法

    public <T> void f(T x) {
              System.out.print("3");
          }
    

    您可以在Overriding and Hiding Methods找到更多信息

    【讨论】:

      【解决方案5】:

      问题是因为方法f(String)对于SomeClass中的类型B不明确,可以这样解决

      public class SomeClass {
          public static class A {
              public void f(int x) {
                  System.out.print("1");
              }
      
              public void f(Object x) {
                  System.out.print("2");
              }
          }
      
          public static class B extends A {
              public void f(Object x) {
                  System.out.print("3");
              }
          }
      
          public static void main(String[] args) {
              A a = new A();
              B b = new B();
              a.f(3);
              a.f("hello");
              b.f(3);
              b.f("hello");
          }
      }
      

      并且会起作用,希望有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-17
        • 1970-01-01
        • 1970-01-01
        • 2011-03-06
        • 2021-12-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多