为什么不允许在重写方法中使用参数类型(一个是原始的而另一个是包装器)的区别?

时间:2015-12-02 13:46:10

标签: java inheritance polymorphism

我最近开始使用JAVA编程并且有一个问题要问。 假设我有一个SuperClass和一个SubClass,它扩展了SuperClass并尝试覆盖SuperClass中定义的方法,如下所示:

  public class SuperClass{
    public void method1(int val){
      System.out.println("This is method in SuperClass.Val is:" + val);
      }
  }

我尝试在我的SubClass中扩展SuperClass并使用一个例外覆盖该方法,而不是在method1中声明的类型int,我将参数的类型用作Integer,如下所示:

  public class SubClass extends SuperClass{
    @Override 
    public void method1(Integer val){     ///compiler gives an error

     }
  }  

编译器不允许使用SubClass方法的声明(我使用的是eclipse IDE)。为什么会这样?整数本质上是int的包装,那么为什么禁止这样的声明?

由于

4 个答案:

答案 0 :(得分:3)

正式解释,正如您可能已经理解的那样,这两个函数具有不同的签名(正如Andrew Tobilko已经指出的那样),因此可能无法覆盖另一个。

所以,我认为通过提出这个问题,你真正要问的是“为什么会这样”,或者“为什么编译器无法解决问题以便让我这样做”。

所以,实际解释如下:

这是因为你可能在某个地方(某些不相关的类甚至)有一些方法接受SuperClass作为参数,并试图像这样调用它的method1()

public void someMethod( SuperClass s )
{
    s.method1( 7 );
}

当编译器发现这一点时,它会将7作为参数传递给method1(),它不会传递包裹的7。但是,s可能不是SuperClass的实例,它可能是SubClass的实例,如下所示:

/* some code somewhere */
someMethod( new SubClass() );

这是有效的,因为OOP中的原则称为Liskov Substitution Principle,表示

  

如果S是T的子类型,则类型T的对象可以用类型S的对象替换(即,类型S的对象可以替换类型为T的对象),而不改变该程序的任何期望属性(正确性) ,任务执行等。)。

这个原理允许你将new ArrayList<String>()分配给List<String>类型的变量,如果没有它,OOP中什么也行不了。

因此,编译器必须传递一个普通的原始7,但SubClass的接收方法会期望一个包裹7,这是行不通的。因此,该语言规定这种隐式转换是无效的,以确保不会出现这种无意义的情况。

<强>修订

你可能会问,“为什么它不起作用?”答案是java中的原语对应于底层硬件的机器数据类型,而包裹的原语是对象。因此,在机器堆栈上,7将由值7的机器字表示,而包裹的7将由0x46d7c8fe表示,它将是一个指向包含包装7的对象的指针。

答案 1 :(得分:2)

这只是因为你在方法@Override之上添加了public void method1(Integer val)语法,它要求编译器在基类中找不到它找不到的相同签名的方法,因此它给出了编译时间错误说明The method method1(Integer) of type SubClass must override a superclass method

了解您提出的问题

  

整数本质上是int的包装,那么为什么禁止这样的声明呢?

因为在任何类中你都可以用这些签名声明方法

class SuperClass
{
    public void method1(int val){
        System.out.println("This is method in SuperClass.Val is:" + val);
    }

    public void method1(Integer val){
        System.out.println("This is method in SuperClass.Val is:" + val);
    }
}

上述声明是有效的,因此@override将尝试将该方法与基类中的精确签名匹配。

答案 2 :(得分:2)

Integer是int的包装器为什么重要?它们仍然是不同的类型。您可以使用具有相同名称的方法,它们只有一个具有基本类型而另一个具有匹配的包装类型。

int和Integer之间的唯一关系是:编译器在任何情况下都会插入一个转换,而另一个则需要转换。

因此,当您引用超类型并调用method1时,编译器会生成基本上传递基元的字节代码。如果在运行时使用实际的SubClass类,则重写的方法不会匹配该调用。

当然编译器可以用允许这种覆盖的样式编写,但它会导致其他问题:

如果决定是在运行时做出的,那么调用overriden方法对装箱/拆箱和检查来说会变得很昂贵,甚至可能会触发NullPointerExceptions

如果通过在编译时将所有内容转换为盒装类型来完成,则此类调用将触发常量装箱拆箱。

这也是禁止你有一个重载方法的构造,要么采用类型参数或基元,因为类型参数可能最终成为该基元的盒装类型,导致两个方法都相同。 / p>

答案 3 :(得分:-3)

Integer和int是不同的。您可以将Integer更改为int,或者删除&#34; @ Override&#34;