【问题标题】:Is it possible to make methods non-virtual in Java?是否可以在 Java 中使方法成为非虚拟方法?
【发布时间】:2020-11-28 11:46:07
【问题描述】:

我是 java 新手,我通常使用 c++ 我只是想知道这段代码是否有效

public class ParentClass {
    public void methodA() {
        System.out.println("This is Parent A Method");
        whoAmI();
    }

    public void methodB() {
        System.out.println("This is Parent B Method and I am Calling Method A");
        whoAmI();
        methodA();
    }
}

public class ChildClass extends ParentClass{
    @Override
    public void methodA() {
        System.out.println("This is Child A Method and I am Calling Parents Method A");
        whoAmI();
        super.methodA();
    }

    @Override
    public void methodB() {
        System.out.println("This is Child B Method and I am Calling Parents Method B");
        whoAmI();
        super.methodB();
    }
}
ChildClass c = new ChildClass();
c.methodB();

我期望这样的输出

This is Child B Method and I am Calling Parents Method B
This is Parent B Method and I am Calling Method A
This is Parent A Method

但是

This is Child B Method and I am Calling Parents Method B
This is Parent B Method and I am Calling Method A
This is Child A Method and I am Calling Parents Method A
This is Parent A Method

所以我意识到在 java 中所有非静态方法都是默认的虚函数,如 c++ 中的 virtual 关键字。

有什么办法可以这样输出吗?

This is Child B Method and I am Calling Parents Method B
This is Parent B Method and I am Calling Method A
This is Parent A Method

【问题讨论】:

  • 代码 - 按原样 - 不会编译。方法whoAmI() 未定义。
  • 他只是忘了给我们看,没什么大不了的
  • 如果你想总是在ParentmethodB中调用Parent类的methodA,那么你可以在Parent中声明methodAfinalprivate ,防止它被覆盖。如果这不是一个选项,那么由于dynamic dispatch(我的C++知识有点生疏,但iirc,C++中的行为应该是一样的),那么就没有办法做你想做的事。
  • whoAmI() 只是简单的显示功能。 public void whoAmI() { System.out.println("我是 B 的实例"); }
  • 这里唯一的问题是为什么你期望输出。这是不正确的,只要方法是 virtual(Java 中的默认值),C++ 的行为方式与此处的 Java 相同。

标签: java class inheritance virtual


【解决方案1】:

由于方法 whoAmI() 未定义,因此代码无法编译。通过删除对该方法的调用,I was able to reproduce the output


据我了解,您希望确保从methodB 中的Parent 中调用Parent 中的methodA。由于在 Jjava 中使用dynamic dispatching 来确定调用哪个实际实现,因此我们必须强制不能覆盖methodA。正如@MarquisofLorne 指出的那样,如果methodAParent 中定义为virtual,C++ 也使用动态调度。

为了强制动态调度始终导致调用ParentmethodA,我想到了两种通用方法:将methodA 声明为final 并将methodA 声明为@987654339 @。


方法一:将Parent中的methodA声明为final

当沿着这条路线前进时,Child 无法重新定义methodA,即如果没有从Child 中删除methodA,代码将导致编译时错误。

Ideone demo


方法二:将Parent中的methodA声明为private

这种方法类似于第一种方法,但允许您在Child 中保留methodA,尽管必须删除@Override-annotation,因为不能覆盖私有方法。

Ideone demo

【讨论】:

  • C++ 仅在指定 virtual 时使用动态调度,但它是 Java 中的默认设置。这可能是 OP 问题的根源。
  • @MarquisofLorne 呵呵...正如我所说...我的 C++ 生锈了 =) 将此信息添加到我的答案中。谢谢!
【解决方案2】:

你得到了:

This is Child B Method and I am Calling Parents Method B
This is Parent B Method and I am Calling Method A
This is Child A Method and I am Calling Parents Method A
This is Parent A Method 

代替

This is Child B Method and I am Calling Parents Method B
This is Parent B Method and I am Calling Method A
This is Parent A Method

仅仅是因为,在 Java 中调用 methodB() 中的 methodB()ParentClass 中的 methodA()this.methodA() 调用了调用对象或实例的方法,但实例是 ChildClass 的实例。 Java 将methodA()this.methodA() 视为相同。 Java 中的this 关键字实际上是指“您正在使用的对象的当前运行实例”。这意味着,将调用 ChildClass 的覆盖 methodA() 而不是 ParentClass 的 methodA()

这里有一个建议,要获得预期的输出,只需通过注释掉打印行来对覆盖 ChildClassmethodA() 进行修改。 否则请参考this 了解更多详情。

【讨论】:

    【解决方案3】:

    您可以将methodAParentClass 的实现包装在单独的私有方法中:

    public class ParentClass {
        public void methodA() {
            interMethodA();
        }
    
        private void interMethodA() {
            System.out.println("This is Parent A Method");
            whoAmI();
        }
    
        public void methodB() {
            System.out.println("This is Parent B Method and I am Calling Method A");
            whoAmI();
            interMethodA();
        }
    }
    

    ChildClass 保持不变。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-21
      • 1970-01-01
      • 2013-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 1970-01-01
      相关资源
      最近更新 更多