抽象方法和虚拟方法有什么区别?在哪些情况下,建议使用抽象方法或虚拟方法?哪一种是最好的方法?


当前回答

在这里,我写了一些示例代码,希望这可能是一个非常实际的例子,可以从非常基本的层面上看到接口、抽象类和普通类的行为。如果您想将其用作演示,也可以在github中找到此代码作为项目:https://github.com/usavas/JavaAbstractAndInterfaceDemo

public interface ExampleInterface {

//    public void MethodBodyInInterfaceNotPossible(){
//    }
    void MethodInInterface();

}

public abstract class AbstractClass {
    public abstract void AbstractMethod();

    //    public abstract void AbstractMethodWithBodyNotPossible(){
    //
    //    };

    //Standard Method CAN be declared in AbstractClass
    public void StandardMethod(){
        System.out.println("Standard Method in AbstractClass (super) runs");
    }
}

public class ConcreteClass
    extends AbstractClass
    implements ExampleInterface{

    //Abstract Method HAS TO be IMPLEMENTED in child class. Implemented by ConcreteClass
    @Override
    public void AbstractMethod() {
        System.out.println("AbstractMethod overridden runs");
    }

    //Standard Method CAN be OVERRIDDEN.
    @Override
    public void StandardMethod() {
        super.StandardMethod();
        System.out.println("StandardMethod overridden in ConcreteClass runs");
    }

    public void ConcreteMethod(){
        System.out.println("Concrete method runs");
    }

    //A method in interface HAS TO be IMPLEMENTED in implementer class.
    @Override
    public void MethodInInterface() {
        System.out.println("MethodInInterface Implemented by ConcreteClass runs");

    //    Cannot declare abstract method in a concrete class
    //    public abstract void AbstractMethodDeclarationInConcreteClassNotPossible(){
    //
    //    }
    }
}

其他回答

抽象方法是一种必须实现才能生成具体类的方法。声明在抽象类中(任何具有抽象方法的类都必须是抽象类),并且必须在具体类中实现。

虚拟方法是一种可以在派生类中使用重写重写的方法,替换超类中的行为。如果不覆盖,则获得原始行为。如果你这样做,你总会得到新的行为。这与不能重写但可以隐藏原始方法的非虚拟方法相反。这是使用新修改器完成的。

请参见以下示例:

public class BaseClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }


    public virtual void SayGoodbye()
    {
        Console.WriteLine("Goodbye");
    }

    public void HelloGoodbye()
    {
        this.SayHello();
        this.SayGoodbye();
    }
}


public class DerivedClass : BaseClass
{
    public new void SayHello()
    {
        Console.WriteLine("Hi There");
    }


    public override void SayGoodbye()
    {
        Console.WriteLine("See you later");
    }
}

当我实例化DerivedClass并调用SayHello或SayGoodbye时,我会得到“Hi There”和“See you later”。如果我打电话给HelloGoodbye,我会收到“你好”和“再见”。这是因为SayGoodbye是虚拟的,可以被派生类替换。SayHello只是隐藏的,所以当我从基类调用它时,我会得到原始方法。

抽象方法是隐式虚拟的。它们定义了必须存在的行为,更像是一个接口。

上面的大多数示例都使用代码,而且非常好。我不需要补充他们所说的内容,但以下是一个简单的解释,使用了类比而不是代码/技术术语。

简单解释-使用类比进行解释

抽象方法

想想乔治·W·布什。他对士兵们说:“去伊拉克打仗吧”。就这样,他所说的就是必须战斗。他没有具体说明这将如何发生。但我的意思是,你不能只是出去“战斗”:那到底意味着什么?我是用B-52还是我的人字架?这些具体细节留给其他人。这是一种抽象方法。

虚拟方法

大卫·彼得雷乌斯在军队中地位很高。他定义了战斗的含义:

找到敌人中和他。之后喝杯啤酒

问题是这是一种非常通用的方法。这是一个有效的好方法,但有时不够具体。对彼得雷乌斯来说,好事是他的命令有回旋余地和范围——他允许其他人根据他们的特殊要求改变他对“战斗”的定义。

私人职业博客阅读彼得雷乌斯的命令,并根据他的特殊要求获得了实施自己版本的战斗的许可:

找到敌人。朝他的头部开枪。回家吧喝啤酒。

努里·马利基也收到彼得雷乌斯的同样命令。他也要战斗。但他是一名政治家,而不是一名步兵。显然,他不能四处射击他的政治敌人的头部。因为彼得雷乌斯给了他一种虚拟的方法,所以马利基可以根据自己的具体情况,实施自己的战斗方法:

找到敌人。让他以一些捏造的罪名被捕。回家吧喝啤酒。

换言之,虚拟方法提供了样板指令——但这些是一般指令,可以由军队继承人根据他们的具体情况做出更具体的说明。

两者之间的区别

乔治·布什没有证明任何实施细节。这必须由其他人提供。这是一种抽象方法。另一方面,彼得雷乌斯确实提供了实施细节,但他已经允许他的下属,如果他们能想出更好的办法,可以用自己的版本推翻他的命令。

希望这会有所帮助。

图.-命题的传统三重分类。

在道义逻辑(义务和许可的研究)中,每个命题都是强制性的(“必须”运算符)、可选的(“可能”运算符)或不允许的(“不得”运算符),任何命题都不属于这三类中的一类。

此外,允许(“可能”运算符)命题是强制性或可选的命题,不允许(“未必”运算符)的命题是不允许或可选的,非可选(“必须或不得”运算符)是强制性或不允许的命题。

特别是,强制性命题是允许的,而不允许的命题是不允许的。

将这些运算符应用于命题“方法被覆盖”会产生以下命题:

抽象(纯)/具体方法:该方法必须被重写/不能被重写;virtual/real(final)方法:该方法可以被重写/不能被重写。

特别是,抽象方法是虚拟的,而真实方法是具体的。

抽象函数“只是”签名,没有实现。它在接口中用于声明如何使用类。它必须在其中一个派生类中实现。

虚函数(实际上是方法)也是您声明的函数,应该在继承层次结构类之一中实现。

此类类的继承实例也继承实现,除非您在较低层次结构类中实现它。

抽象方法没有实现。它在父类中声明。子类负责实现该方法。

虚拟方法应该在父类中有一个实现,它有助于子类选择是使用父类的该实现,还是为子类中的该方法自己有一个新的实现。