您可以用Java编写虚拟函数/方法吗?


165

是否可以像用C ++一样用Java 编写虚拟方法?

或者,是否存在可以实现类似行为的适当Java方法?我可以举一些例子吗?

Answers:


305

来自维基百科

Java中,默认情况下,所有非静态方法都是“ 虚拟函数”。只有标记为final的方法(不能被覆盖)以及 私有方法(未继承)都是非虚拟的


3
乔恩·斯基特Jon Skeet)的答案之一。
Quazi Irfan

我wonded如果这是真的,因为我读过,在Java中,动态方法调度只发生在方法被调用的对象-如解释在这里如此的例子说明虚函数的C ++ 这里是无效的Java。
西兰花

@QuaziIrfan那是Java和C#之间的区别。
Sreekanth Karumanaghat

101

可以用Java编写虚拟函数吗?

是。实际上,默认情况下,Java中的所有实例方法都是虚拟的。只有某些方法不是虚拟的:

  • 类方法(因为通常每个实例都保存有关其特定方法的信息,例如指向vtable的指针,但此处没有可用的实例)。
  • 私有实例方法(由于没有其他类可以访问该方法,因此调用实例始终具有定义类本身的类型,因此在编译时明确地知道该实例)。

这里有些例子:

“正常”虚拟功能

以下示例来自另一个答案中提到的Wikipedia页面的旧版本

import java.util.*;

public class Animal 
{
   public void eat() 
   { 
      System.out.println("I eat like a generic Animal."); 
   }

   public static void main(String[] args) 
   {
      List<Animal> animals = new LinkedList<Animal>();

      animals.add(new Animal());
      animals.add(new Fish());
      animals.add(new Goldfish());
      animals.add(new OtherAnimal());

      for (Animal currentAnimal : animals) 
      {
         currentAnimal.eat();
      }
   }
}

class Fish extends Animal 
{
   @Override
   public void eat() 
   { 
      System.out.println("I eat like a fish!"); 
   }
}

class Goldfish extends Fish 
{
   @Override
   public void eat() 
   { 
      System.out.println("I eat like a goldfish!"); 
   }
}

class OtherAnimal extends Animal {}

输出:

我像普通动物一样吃东西。
我吃得像鱼!
我吃得像一条金鱼!
我像普通动物一样吃东西。

带接口的虚函数示例

Java 接口方法都是虚拟的。它们必须是虚拟的,因为它们依赖于实现类来提供方法实现。仅在运行时选择要执行的代码。

例如:

interface Bicycle {         //the function applyBrakes() is virtual because
    void applyBrakes();     //functions in interfaces are designed to be 
}                           //overridden.

class ACMEBicycle implements Bicycle {
    public void applyBrakes(){               //Here we implement applyBrakes()
       System.out.println("Brakes applied"); //function
    }
}

带有抽象类的虚函数示例。

与接口类似,抽象类 必须包含虚拟方法,因为它们依赖扩展类的实现。例如:

abstract class Dog {                   
    final void bark() {               //bark() is not virtual because it is 
        System.out.println("woof");   //final and if you tried to override it
    }                                 //you would get a compile time error.

    abstract void jump();             //jump() is a "pure" virtual function 
}                                     
class MyDog extends Dog{
    void jump(){
        System.out.println("boing");    //here jump() is being overridden
    }                                  
}
public class Runner {
    public static void main(String[] args) {
        Dog dog = new MyDog();       // Create a MyDog and assign to plain Dog variable
        dog.jump();                  // calling the virtual function.
                                     // MyDog.jump() will be executed 
                                     // although the variable is just a plain Dog.
    }
}

1
这必须是最完整的答案。由于java没有关键字,因此它提供了两种方法来实现虚拟功能。谢谢。
Christopher Bales 2014年

比维基百科的引用要好得多。来自c ++并懒于Java学习,我一直在寻找抽象。
大卫

@David这个答案更好吗?维基百科的引用是完整,简洁和正确的。相比之下,此答案没有提到房间里的大象:默认情况下,Java中的所有功能(维基百科文章中列出的例外)都是虚拟的。虚函数既不需要抽象类也不需要接口,因此只会增加误导性的噪音。然后,“这需要出色的沟通技巧和对基本原理的深刻理解”……jeez。那是一个自我伪造的说法:没有谁会浪费宝贵的磁盘空间。
彼得-恢复莫妮卡

Wikipedia帖子相对于此答案而言较为逊色,因为它涉及的是任何语言(而不只是Java)中的虚函数概念。Wikipedia页面中给出的示例是用C编写的,充其量只是一个不完整的示例,而且更具误导性。关于所有函数都是虚拟的,并且不需要抽象类或接口就可以使虚拟函数成为噪音的细节。我从未说过它们是必需的,您误读了。我不明白您的意思,您是否要我删除这个问题,因为您不喜欢它?
Eric Leschinski 2015年

1
这里晚了几年,但答案很不错
Tom O.19年

55

Java中的所有功能默认情况下都是虚拟的。

您必须通过添加“ final”关键字来编写非虚拟函数。

这与C ++ / C#默认设置相反。默认情况下,类函数是非虚拟的;您可以通过添加“虚拟”修饰符来实现。


4
克劳斯的回答中提到的私有功能也是非虚拟的。
Don Larynx

9

默认情况下,所有非私有实例方法在Java中都是虚拟的。

在C ++中,私有方法可以是虚拟的。可以将其用于非虚拟接口(NVI)习惯用法。在Java中,您需要使NVI可重写方法受到保护。

从Java语言规范v3:

8.4.8.1覆盖(通过实例方法)如果满足以下所有条件,则在类C中声明的实例方法m1将覆盖在类A中声明的另一个实例方法m2:

  1. C是A的子类。
  2. m1的签名是m2的签名的子签名(第8.4.2节)。
  3. * m2是公共的,受保护的或被声明为具有与C相同的程序包中的默认访问权限,或者* m1覆盖了方法m3,与m1不同的m3,与m2不同的m3,从而m3覆盖了m2。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.