URLhttps://learnscript.net/zh/programming/object-oriented/class-polymorphism/
    复制链接转到说明  示例

    类的多态性,方法重写,方法重载,方法隐藏介绍

    我被代码海扁署名-非商业-禁演绎
    阅读 9:13·字数 2767·发布 

    前提

    阅读本节的前提是对面向对象编程,类,实例等概念有所掌握,你可以查看面向对象编程,类,实例介绍一节来了解他们。

    类的多态性

    类的多态性是指派生类可以改变调整继承成员的实现,以符合自身的需求,这主要针对实例方法,或本质上是方法的实例成员。多态性使得继承与被继承的对象,在同一功能上的表现可以不同。

    变量类型与多态性

    在支持面向对象和声明变量类型的语言中,变量的类型与值的数据类型可能不同,通过变量调用的方法,将指向对象对该方法的真实实现,当对象已经对方法做出调整或改变时。

    在下面的 C# 代码中,虽然变量kittyanimal的类型都是Animal,且Animal类的Bark方法固定,但实际调用Bark方法却显示了不同的信息,因为变量kitty对应的真实对象的类型是Kitty,对于Bark方法,Kitty类拥有自己的实现。

    *.cs
    // 创建 Animal 和 Kitty 的实例
    Animal animal = new Animal();
    Animal kitty = new Kitty();
    
    // 分别调用 Bark 方法,将显示不同信息 Console.WriteLine("animal.Bark"); animal.Bark(); Console.WriteLine("kitty.Bark"); kitty.Bark();
    animal.Bark
    试着发出声音?
    kitty.Bark
    试着发出声音?
    喵喵喵!

    方法重写

    方法重写(Method Overriding)是实现多态性的最简单方式,一般的做法是在类中再次定义需要重写的实例方法,此过程可能需要特定的关键字,比如,C# 中的override

    什么是虚方法?

    虚方法是指允许被派生类重写的方法,通常对应关键字virtual。在一些语言中,虚方法是不存在的,因为默认所有方法均可被重写。

    由于虚拟方法允许被重写,所以他不应该具有任何阻止该目标实现的特征,比如,私有的。

    在派生类中调用基类的方法

    在方法重写的过程中,你可能需要使用基类的方法,如果重写的目的在于补充功能。大部分语言会提供相关的关键字来完成此目标,比如,C# 中的base关键字。

    下面展示了之前示例中使用的类AnimalKitty,可以看到Animal类拥有一个虚方法BarkKitty类对该方法进行了重写,并调用了基类的Bark方法。

    class_polymorphism.cs
    // 基类 Animal
    class Animal
    {
    	// 虚方法 Bark,尝试发出声音
    	public virtual void Bark()
    	{
    		Console.WriteLine("试着发出声音?");
    	}
    }
    
    // 派生类 Kitty class Kitty : Animal { // Kitty 重写了继承自 Animal 的方法 Bark public override void Bark() { // 调用基类的 Bark 方法 base.Bark();
    Console.WriteLine("喵喵喵!"); } }

    什么是抽象方法和抽象类?

    抽象方法是类中已经声明,但尚未具体实现的方法,他通常需要特定的关键字来标注,比如,C# 中的abstract,并需要派生类来完成实现,一个方法被抽象的原因是其必要性和缺少通用性的逻辑。

    由于需要通过派生类完成实现,抽象方法不能具有任何阻止这一目标实现的特征,比如,私有的。

    拥有抽象方法的类被称为抽象类,因为没有完全的具体实现,抽象类无法被实例化。需要指出,抽象类包括了,已继承抽象方法但仍未对其实现的派生类。

    抽象方法和虚方法之间的区别

    抽象方法要求被具体实现,否则相关类无法实例化,虚方法则表示允许被重写,能否实例化与是否被重写无关。

    下面,为Animal类增加一个抽象方法RunKitty类需要实现该方法后才能被实例化。

    class_polymorphism.cs
    // 基类 Animal
    abstract class Animal
    {
    	// …
    	// 抽象方法 Run,奔跑起来
    	public abstract void Run();
    }
    
    // 派生类 Kitty class Kitty : Animal { // … // Kitty 必须实现抽象方法 Run,才能被实例化 public override void Run() { Console.WriteLine("你看,我跑起来了!"); } }
    *.cs
    // 创建 Kitty 的实例
    Kitty kitty = new Kitty();
    
    // 调用 Run 方法 kitty.Run();
    你看,我跑起来了!

    方法重载

    方法重载(Method Overloading)与多态性的关系不大,他允许一个类定义多个名称相同但签名不同的方法,而不是将基类中的方法再次定义。方法重载在一些语言中是不被支持或不可行的,他们不允许一个类拥有名称相同的方法,方法签名仅通过方法名称产生,或者根本没有签名。

    重写基类的重载方法需要匹配方法签名

    这里需要指出,在重写基类的重载方法时,请注意方法签名是否匹配。

    选择方法重载还是可选参数?

    当方法内部的逻辑差异不大时,使用可选参数应该是更好,如果语言支持这一功能的话,你将不会看到一排排的方法定义,仅仅是为了多出或缺少的参数。

    当不同参数组合带来的逻辑差异较大时,可选择使用方法重载,以使代码更容易维护。

    函数

    要了解方法签名,你可以查看函数签名一段,好吧,名字不代表一切。

    下面,通过重载为Kitty类添加两个同名的Eat方法,他们拥有的参数不同。

    class_polymorphism.cs
    // 派生类 Kitty
    class Kitty : Animal
    {
    	// …
    	// 使用重载定义两个同名的 Eat 方法
    	public void Eat(string something)
    	{
    		Console.WriteLine($"好耶,今天的午餐是:{something}");
    	}
    	public void Eat()
    	{
    		Console.WriteLine("今天吃空气?!");
    	}
    }
    *.cs
    // 创建 Kitty 的实例
    Kitty kitty = new Kitty();
    
    // 调用重载的 Eat 方法 kitty.Eat("小鱼干"); kitty.Eat();
    好耶,今天的午餐是:小鱼干
    今天吃空气?!

    方法隐藏

    方法隐藏(Method Hiding/Shadowing)和方法重写是比较容易混淆的,他们均针对基类的方法进行重新定义,但不同的是,通过隐藏定义的方法断绝了自身与基类方法的继承关系,通过重写定义的方法则会保持。方法隐藏和方法重写的选择,将影响多态性的表现效果。

    在通过某种类型的变量调用方法时,需要确定实际对象是否存在对该方法的重写,而通过隐藏定义的方法将打断这一过程,前提是这些方法真实的阻断了当前和之后的继承关系,他们不能是私有的,或具有任何导致上述目标无法实现的特征。

    在下面的 C# 代码中,变量abcd的类型均为A,但变量b对应的真实对象的类型为B,类B重写了类AShow方法,因此b.Show()指向类BShow方法的实现。变量c对应的真实对象的类型为C,类C隐藏了类BShow方法,c.Show()将不会指向类CShow方法的实现,因为类CShow方法与类BAShow方法不存在继承关系。变量d对应的真实对象的类型为D,类D重写了类CShow方法,d.Show()将不会指向类DCShow方法的实现,因为类DCShow方法与类BAShow方法不存在继承关系。

    *.cs
    // 所有变量的类型均为 A,但真实的对象类型分别为 A,B,C,D
    A a = new A();
    A b = new B();
    A c = new C();
    A d = new D();
    
    // 分别调用他们的 Show 方法 a.Show(); b.Show(); c.Show(); d.Show();
    调用了 A 的 Show 方法
    调用了 B 的 Show 方法
    调用了 B 的 Show 方法
    调用了 B 的 Show 方法
    class_polymorphism.cs
    // 类 A
    class A
    {
    	// 虚方法 Show
    	public virtual void Show()
    	{
    		Console.WriteLine("调用了 A 的 Show 方法");
    	}
    }
    
    // 类 B class B : A { // 重写类 A 的 Show 方法 public override void Show() { Console.WriteLine("调用了 B 的 Show 方法"); } }
    // 类 C class C : B { // 隐藏类 B 的 Show 方法,阻断了继承关系 public new virtual void Show() { Console.WriteLine("调用了 C 的 Show 方法"); } }
    // 类 D class D : C { // 重写类 C 的 Show 方法 public override void Show() { Console.WriteLine("调用了 D 的 Show 方法"); } }

    源码

    class_polymorphism.cs·codebeatme/programming·GitHub