【问题标题】:Whose method gets overridden when a derived class extends a base class and at the same time implements an interface?当派生类扩展基类并同时实现接口时,谁的方法会被覆盖?
【发布时间】:2018-09-21 14:44:11
【问题描述】:

案例一:

我有一个Inter.java 接口:

interface Inter
{
        void abcd();
}

一个基类:

class Base
{
        void abcd()
        {
                System.out.println("Base abcd");
        }
}

派生类:

class Use extends Base implements Inter
{
        void abcd()  //intentionally not using public 
        {
                System.out.println("Use abcd");
        }
        public static void main(String[] args)
        {
                Use u = new Use();
                u.abcd();
        }
}

现在我编译它:

gyan@#ns:~/Desktop$ javac Inter.java Base.java Use.java 
Use.java:3: error: abcd() in Use cannot implement abcd() in Inter
    void abcd()
         ^
  attempting to assign weaker access privileges; was public
1 error
gyan@#ns:~/Desktop$ 

这意味着 Use 类中重写的方法 abcd() 来自实现的接口“Inter”,而不是来自基类“Base”。

案例 2:

我们有一个包含以下代码的文件 Test.java:

interface Ab
{
        void run();
}

class A extends Thread implements Ab
{
        public void run()
        {
                System.out.println("class A");
        }
        public static void main(String[] args)
        {
                A a = new A();
                Thread t = new Thread(a);
                t.start();
                System.out.println("main method");
        }
}

当我们执行它时,我们得到:

gyan@#ns:~/Desktop$ java A
main method
class A
gyan@#ns:~/Desktop$

由于 t.start() 执行了方法 run(),这意味着类 Thread 的方法 run() 被覆盖了。但在案例 1 中,接口“Inter”的方法 abcd() 被覆盖。

案例 1:在 Use 类中重写了谁的方法 abcd()?类基或接口间?错误说我们正在覆盖接口 Inter 的 abcd()。但是在案例2中:似乎我们没有覆盖接口Ab的方法run。因为我们可以通过调用 t.start() 来执行 run() 方法。这只有在我们重写 Thread 类的 run() 时才有可能。

【问题讨论】:

  • @OHGODSPIDERS 具有默认可见性的方法对同一包中的所有类都是可见的。所以你错了。

标签: java inheritance interface overriding


【解决方案1】:

Inter接口中,void abcd();是一个抽象方法的定义。所有抽象方法本质上都是公共的。

来自 {Defining an Interface},

接口中的所有抽象、默认和静态方法都是隐式公共的,因此您可以省略 public 修饰符。

所以,虽然Interabcd()public,但Use 中的实际实现是说abcd() 的实现是package-private,这是一个较弱的访问权限。这应该解决您的评论 "//intentionally not using public"
传统上,abcd() 也应该使用注释 @Override(尽管这不是硬性要求)。

在您的第二个示例中,Thread 类提供了run() 的具体实现。鉴于A#run() 是公共的,您已经满足接口Ab 对访问说明符的要求。然后,A#run() 继续覆盖Thread#run()——这正是我们期望继承的行为方式。

总而言之,我们刚刚看到extends(扩展或覆盖类方法)和implements(约定给定类提供接口中所有方法状态的实现)之间的区别。 {This answer} 对此进行了更深入的介绍,是一本优秀的读物。

如果此答案的任何部分需要更清晰,请告诉我。

【讨论】:

  • 案例 1:使用类中重写了谁的方法 abcd()?类基或接口间?错误说我们正在覆盖接口 Inter 的 abcd()。但是在案例2中:似乎我们没有覆盖接口Ab的方法run。因为我们可以通过调用 t.start() 来执行 run() 方法。这只有在我们重写 Thread 类的 run() 时才有可能。
【解决方案2】:

您的类中的方法会覆盖基类方法并同时实现接口。

重写方法与实现接口并不完全相同:

  • 覆盖超类中的方法意味着您正在替换其实现。如果您不提供覆盖方法,则将使用超类中的实现(可以为空)。
  • 在实现接口方法时,您正在为简单声明提供代码(没有任何实现)。如果你声明一个非抽象类实现了一个接口,但没有为接口的每个方法提供一个实现,编译就会失败。这在 Java 8 中不太正确,您可以选择在接口中提供默认实现,但在这种情况下,任何来自超类的实现都会胜出。

方法的实现和覆盖总是可以扩展它的可见性,但不能降低它。这意味着:

  • 超类中的包私有(默认可见性)或受保护方法可以在子类中公开
  • 但超类中的公共方法不能在子类中设置为包私有或受保护。

接口中的方法始终是公共的,即使接口中没有指定“public”修饰符。因此,它们的实现也必须是公开的 => 这就是您在案例 1 中遇到的错误 1

附带说明,在您的情况 2 中,您使用错误的线程。不要子类化 Thread。相反,创建一个实现 Runnable 或 Callable 接口的类,并将其提交到线程池(参见 java.util.concurrent 中的类,或使用 java 8 的ForkJoinPool.commonPool().submit(myTask) 进行快速测试。

【讨论】:

    【解决方案3】:

    两者兼而有之。方法覆盖取决于方法的名称和签名,而不是它的定义位置。如果在多个祖先上定义,它将覆盖两者。 注意:如果在一个中定义为公共而在另一个中定义为受保护,它将变为公共。

    【讨论】:

      猜你喜欢
      • 2013-09-30
      • 2016-06-14
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-22
      • 2019-05-22
      相关资源
      最近更新 更多