多态性vs覆盖vs重载


347

就Java而言,当有人问:

什么是多态性?

超载重载是一个可以接受的答案?

我认为还有更多。

如果您有一个抽象基类,它定义了一个没有实现的方法,并且您在子类中定义了该方法,那该方法是否仍然覆盖?

我认为 过载不是肯定的正确答案。


以下答案很好地说明了多态性。但是我强烈反对说重载是一种多态性,我在我的问答中试图证明其合理性,而实际上专注于重载是多态性与否。我试图证明此线程中存在@The Digital Gabeg答案。请参阅详细说明:方法重载是静态/编译时绑定,但不是多态。将静态绑定与多态相关联是否正确?
PraveenKumar Lalasangi '19

Answers:


894

表达多态性的最清晰方法是通过抽象基类(或接口)

public abstract class Human{
   ...
   public abstract void goPee();
}

此类是抽象的,因为 goPee()方法不适用于人类。只有男性和女性子类别才可以定义。同样,人类是一个抽象的概念—您不能创造既不是男性也不是女性的人类。它必须是另一个。

因此,我们通过使用抽象类推迟实现。

public class Male extends Human{
...
    @Override
    public void goPee(){
        System.out.println("Stand Up");
    }
}

public class Female extends Human{
...
    @Override
    public void goPee(){
        System.out.println("Sit Down");
    }
}

现在我们可以告诉充满人类的整个房间撒尿。

public static void main(String[] args){
    ArrayList<Human> group = new ArrayList<Human>();
    group.add(new Male());
    group.add(new Female());
    // ... add more...

    // tell the class to take a pee break
    for (Human person : group) person.goPee();
}

运行此将产生:

Stand Up
Sit Down
...

37
@yuudachi。我在教课时想出了这个例子。规范的“银行帐户”类并未真正表达基类的“抽象性”。另一个典型的例子(动物,制造噪音)太抽象了,无法理解。我一直在寻找一个包含明显子类的单一基础。实际上,goPee()是我想出的唯一例子,它不是性别歧视或陈规定型观念。(尽管在课堂上,我在左边的大厅里打印了下来,而不是站起来或坐下。)
克里斯·库德莫

100
该示例还很好地突出了使用分层系统描述生物系统的困难。某些人(例如非常小的人)几乎可以在任何位置撒尿-不能轻易地告诉婴儿goPee()。一些人是双性恋,“男性”或“女性”的生物学标记变得非常模糊。社会意义更加复杂。作为一个教学示例,它显示了建模假设如何产生负面结果,例如暗示失禁或双性恋的某个人(例如,OO编程的学生)实际上不是人类。
Andrew Dalke

7
我可以想到至少有少数人会反驳您的“您不能创建一个既不是男性也不是女性”的论文,尽管您的代码仍然是正确的……我想我说的是糟糕的抽象?;)
Frank W. Zammetti

2
我认为必须指出这只是多态性,因为只能在运行时确定要调用的goPee()版本。尽管此示例暗示了这一点,但还是很好地指出了为什么正是多态性。另外,它不需要同级类。也可以是亲子关系。甚至是完全不相关的类,它们恰好具有相同的功能。.toString()函数就是一个例子。可以在任何对象上随机调用哪个对象,但是编译器永远无法确切知道对象的类型。
Tor Valamo

20
@ AndrewDalke,+ 1表示关于生物复杂性的注释。另外,goPee不要将重力场作为输入。这种对全局状态的依赖使单元测试CatheterizedIntersexAstronaut变得困难,并表明子类化可能并不总是构成特征的最佳方法。
Mike Samuel

99

多态是类实例的行为,就好像它是其继承树中的另一个类的实例一样,通常是其祖先类之一。例如,在Java中,所有类都从Object继承。因此,您可以创建对象类型的变量,并为其分配任何类的实例。

一个覆盖是出现在从另一个类继承的一个类中的一种函数。覆盖函数“替换”了从基类继承的函数,但是这样做的方式是,即使其类的实例通过多态性假装为其他类型,也可以调用该函数。参考前面的示例,您可以定义自己的类并覆盖toString()函数。因为此函数是从Object继承的,所以如果将此类的实例复制到Object-type变量中,该函数仍然可用。通常,如果在假装为Object的类上调用toString(),则实际上将触发的toString版本是在Object本身上定义的版本。但是,由于该函数是一个覆盖,因此即使在类实例为“

重载是定义多个具有相同名称但具有不同参数的方法的操作。它与覆盖或多态无关。


8
这很老,但是多态并不意味着另一个类必须在继承树中。如果您将接口视为继承树的一部分,则在Java中会这样做,但在Go(隐式实现接口)的Go中则不会。
JN

5
实际上,您根本不需要多态类。
StCredZero

3
我是新手,如果我做错了,请纠正我,但我不会说重载与多态无关。至少在Java中,多态性是根据调用者的类型选择实现的,而重载是根据参数的类型选择实现的,不是吗?看到两者之间的相似性有助于我理解它。
csjacobs24

9
不正确 Ad hoc polymorphism是您在“ 重载”部分中描述的内容,并且多态的情况。
Jossie Calderon

1
“它与覆盖或多态无关”。这个说法是错误的。
Shailesh Pratapwar

45

多态性是指不止一种形式,同一对象根据需要执行不同的操作。

多态可以通过两种方式实现,即

  1. 方法覆盖
  2. 方法重载

方法重载是指使用相同的方法名称在同一类中编写两个或多个方法,但是传递的参数不同。

方法覆盖意味着我们在不同的类中使用方法名称,这意味着在子类中使用父类方法。

在Java中,为了实现多态性,超类引用变量可以保存子类对象。

为了实现多态,每个开发人员必须在项目中使用相同的方法名称。


4
+1为好答案。公认的答案仅解释了一种多态性。这个答案是完整的。
apadana

1
多态是一个范式(OOP),但是覆盖和重载是语言工具。
曾其威

多态性也可以通过泛型类型实现。
MinhNghĩa19年

43

这是伪C#/ Java中多态的示例:

class Animal
{
    abstract string MakeNoise ();
}

class Cat : Animal {
    string MakeNoise () {
        return "Meow";
    }
}

class Dog : Animal {
    string MakeNoise () {
        return "Bark";
    }
}

Main () {
   Animal animal = Zoo.GetAnimal ();
   Console.WriteLine (animal.MakeNoise ());
}

Main函数不知道动物的类型,并且取决于MakeNoise()方法的特定实现的行为。

编辑:看起来布莱恩击败我一拳。有趣的是,我们使用了相同的示例。但是上面的代码应有助于阐明概念。


这是运行时多态性的一个示例。通过方法重载和泛型类型也可以实现编译时多态。
皮特·柯坎

形状->平行四边形->矩形->正方形
mpen 2010年

@ yankee2905在这种情况下,我认为您可以使用接口,因为一个类可以实现多个接口。
Sam003年

1
@Zhisheng还是在抽象父类中添加小便方法?我会使用接口来实现其他功能。
joey rohan 2015年

42

覆盖和重载均用于实现多态。

您可能在一个类中有一个一个或多个子类覆盖的方法。该方法根据使用哪个类实例化对象而做不同的事情。

    abstract class Beverage {
       boolean isAcceptableTemperature();
    }

    class Coffee extends Beverage {
       boolean isAcceptableTemperature() { 
           return temperature > 70;
       }
    }

    class Wine extends Beverage {
       boolean isAcceptableTemperature() { 
           return temperature < 10;
       }
    }

你也可以有一个是方法 重载具有两个或更多套的参数。该方法根据传递的参数的类型执行不同的操作。

    class Server {
        public void pour (Coffee liquid) {
            new Cup().fillToTopWith(liquid);
        }

        public void pour (Wine liquid) {
            new WineGlass().fillHalfwayWith(liquid);
        }

        public void pour (Lemonade liquid, boolean ice) {
            Glass glass = new Glass();
            if (ice) {
                glass.fillToTopWith(new Ice());
            }
            glass.fillToTopWith(liquid);
        }
    }

我认为它被否决了,因为从历史上看,方法重载在面向对象范例中不被视为多态的一部分。方法重载和多态性是编程语言的两个正交的独立特征。
塞尔吉奥·阿科斯塔

7
正如我在此处的回答所述,我不同意-这两个特征不是正交的,而是紧密相关的。多态!=继承。你有我的赞成票。
Peter Meyer

2
换句话说,类型多态性与即席多态性。我赞成这个答案,即使它不尽如人意,也因为它正确地指出了重载和重载都与多态有关。说OOP语言中的多态只能通过类继承来实现是完全错误的-我们应该记住,除了Java和C ++之外,还有其他一些OOP语言可以使用诸如多重调度,临时多态,参数多态等概念。 。
rsenna 2014年

2
@rsenna这可能是不完整的,但它比其他恕我直言的答案要好得多。另外,非常高兴您提到了即席和参数多态性。
Valentin Radu 2015年

15

您是正确的,过载不是答案。

两者都不是最重要的。覆盖是获得多态的手段。多态是对象根据其类型改变行为的能力。当显示多态性的对象的调用者不知道该对象是哪种特定类型时,最好地证明这一点。


3
改变的对象的行为不应该是它的实现。相同的行为,不同的实现,就是多态。
QBziZ

@QBziZ您需要定义行为,尤其是形容词相同。如果行为相同,为什么它们的实现也不同?并非有人对某种实现感到不满意,因此需要一种不同的实现。
Sнаđошƒаӽ

11

具体说重载或覆盖并不能提供全面的信息。多态只是对象根据其类型专门化其行为的能力。

我不同意这里的一些答案,因为在同名方法可以表现不同的情况下,重载是多态(参数多态)的一种形式,它给出了不同的参数类型。一个很好的例子是运算符重载。您可以定义“ +”以接受不同类型的参数(例如字符串或整数),并且基于这些类型,“ +”的行为将有所不同。

多态性还包括继承和覆盖方法,尽管它们在基本类型中可以是抽象的或虚拟的。就基于继承的多态性而言,Java仅支持单类继承,将其多态行为限制为单基类型链的多态行为。Java确实支持实现多个接口,这是多态行为的另一种形式。


就所涉及的单词的一般含义而言,您是正确的,但是在编程环境中,当人们说“多态”时,它们始终表示“基于继承的多态”。有趣的一点是,但我认为以这种方式描述多态会使人们感到困惑。
Digital Gabeg

仅从继承的角度解释多态性可能会更容易,但是我问这个特定问题的方式也是谨慎的,以描述参数多态性也是如此。
Patrick McElhaney

4
明确地说,我认为应该陈述不同的形式-我什至没有适当地做-因为这里有一些答案是绝对的。我谨对此表示不同意,在“程序员的语境下……'多态性'总是意味着'基于继承的多态性'”
Peter Meyer

2
我认为超载更好地归类为广告,hoc_polymorphism en.wikipedia.org/wiki/...
吉诺比利

在以下方面,我倾向于同意“ The Digital Gabeg”。如果您正在讨论OOP,则多态性通常意味着子类型多态性,如果您正在讨论类型理论,则意味着任何类型的多态性。
Manu 2010年

7

多态仅表示“许多形式”。

它不需要继承就可以实现...因为根本不是继承的接口实现满足了多态需求。可以说,接口实现服务于多态需求而不是继承。

例如,您会创建一个超类来描述所有可以飞行的东西吗?我不认为。最好为您创建一个描述航班的界面,然后将其保留。

因此,由于接口描述了行为,而方法名称描述了行为(对程序员而言),因此将方法重载视为一种较小的多态形式并不是一件容易的事。


2
绝对是最好的答案。多态可以应用于所有的语言构造,无论是名词(类)还是动词(方法)。
拉杜·加斯勒

6

经典的例子是,猫狗是动物,动物具有makeNoise方法。我可以遍历一系列对它们调用makeNoise的动物,并期望它们会在那里执行相应的实现。

调用代码不必知道它们是什么特定动物。

那就是我认为的多态性。


4

多态是对象以多种形式出现的能力。这涉及使用继承和虚拟函数来构建可以互换的对象系列。基类包含虚拟函数的原型,可能未实现或根据应用程序的指示具有默认实现,并且各种派生类各自以不同的方式实现它们以影响不同的行为。


4

都不:

重载是当您具有相同的函数名称且采用不同的参数时。

覆盖是指子类用其父类的方法替换其父类的方法时(这本身并不构成多态性)。

多态性是后期绑定,例如,基类(父)方法被调用,但直到应用程序运行时才知道实际对象是什么-它可能是子类,其方法不同。这是因为可以在定义基类的地方使用任何子类。

在Java中,使用collections库可以看到很多多态性:

int countStuff(List stuff) {
  return stuff.size();
}

List是基类,如果您要计算链接列表,向量,数组或自定义列表实现,则编译器不知道,只要它的作用类似于List:

List myStuff = new MyTotallyAwesomeList();
int result = countStuff(myStuff);

如果您超载,您将:

int countStuff(LinkedList stuff) {...}
int countStuff(ArrayList stuff) {...}
int countStuff(MyTotallyAwesomeList stuff) {...}
etc...

编译器将选择正确版本的countStuff()来匹配参数。


4

尽管在这篇文章中已经对多态性进行了详细的解释,但是我想更加强调为什么要包含它。

为什么多态在任何OOP语言中都如此重要。

让我们尝试为有和没有继承/多态性的电视构建一个简单的应用程序。发布应用程序的每个版本后,我们进行一次小型回顾。

假设您是电视公司的软件工程师,并且要求您编写用于Volume,Brightness和Color控制器的软件,以根据用户命令增加或减少其值。

首先,通过添加以下内容为每个功能编写类

  1. 设置:-设置控制器的值(假设它具有控制器特定的代码)
  2. get:-获取控制器的值。(假设它具有控制器特定的代码)
  3. 调整:-验证输入并设置控制器。(通用验证。独立于控制器)
  4. 用户输入与控制器的映射:-获取用户输入并相应地调用控制器。

应用版本1

import java.util.Scanner;    
class VolumeControllerV1 {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class  BrightnessControllerV1 {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class ColourControllerV1    {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

/*
 *       There can be n number of controllers
 * */
public class TvApplicationV1 {
    public static void main(String[] args)  {
        VolumeControllerV1 volumeControllerV1 = new VolumeControllerV1();
        BrightnessControllerV1 brightnessControllerV1 = new BrightnessControllerV1();
        ColourControllerV1 colourControllerV1 = new ColourControllerV1();


        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println("Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV1.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV1.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV1.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV1.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV1.adjust(5);
                    break;
                }
                case 6: {
                colourControllerV1.adjust(-5);
                break;
            }
            default:
                System.out.println("Shutting down...........");
                break OUTER;
        }

    }
    }
}

现在,您已经可以部署我们的第一个工作应用程序版本。是时候分析到目前为止完成的工作了。

电视应用程序版本1中的问题

  1. Adjust(int value)代码在所有三个类中都是重复的。您想最大程度地减少代码重复性。(但是您没有想到通用代码,而是将其移至某些超类以避免重复代码)

只要您的应用程序能够按预期运行,您就决定接受该协议。

有时过后,您的老板会回来找您,并要求您向现有应用程序添加重置功能。重置会将所有三个控制器设置为各自的默认值。

您开始为新功能编写新类(ResetFunctionV2),并为此新功能映射用户输入映射代码。

应用版本2

import java.util.Scanner;
class VolumeControllerV2    {

    private int defaultValue = 25;
    private int value;

    int getDefaultValue() {
        return defaultValue;
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class  BrightnessControllerV2   {

    private int defaultValue = 50;
    private int value;
    int get()    {
        return value;
    }
    int getDefaultValue() {
        return defaultValue;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class ColourControllerV2    {

    private int defaultValue = 40;
    private int value;
    int get()    {
        return value;
    }
    int getDefaultValue() {
        return defaultValue;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

class ResetFunctionV2 {

    private VolumeControllerV2 volumeControllerV2 ;
    private BrightnessControllerV2 brightnessControllerV2;
    private ColourControllerV2 colourControllerV2;

    ResetFunctionV2(VolumeControllerV2 volumeControllerV2, BrightnessControllerV2 brightnessControllerV2, ColourControllerV2 colourControllerV2)  {
        this.volumeControllerV2 = volumeControllerV2;
        this.brightnessControllerV2 = brightnessControllerV2;
        this.colourControllerV2 = colourControllerV2;
    }
    void onReset()    {
        volumeControllerV2.set(volumeControllerV2.getDefaultValue());
        brightnessControllerV2.set(brightnessControllerV2.getDefaultValue());
        colourControllerV2.set(colourControllerV2.getDefaultValue());
    }
}
/*
 *       so on
 *       There can be n number of controllers
 *
 * */
public class TvApplicationV2 {
    public static void main(String[] args)  {
        VolumeControllerV2 volumeControllerV2 = new VolumeControllerV2();
        BrightnessControllerV2 brightnessControllerV2 = new BrightnessControllerV2();
        ColourControllerV2 colourControllerV2 = new ColourControllerV2();

        ResetFunctionV2 resetFunctionV2 = new ResetFunctionV2(volumeControllerV2, brightnessControllerV2, colourControllerV2);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV2.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV2.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV2.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV2.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV2.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV2.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV2.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

因此,您已经准备好具有复位功能的应用程序。但是,现在您开始意识到

电视应用程序版本2中的问题

  1. 如果产品中引入了新的控制器,则必须更改“重置”功能代码。
  2. 如果控制器的数量非常多,则在保存控制器的引用时会出现问题。
  3. 重置功能代码与所有控制器类的代码紧密结合在一起(以获取和设置默认值)。
  4. 重置要素类(ResetFunctionV2)可以访问Controller类的其他方法(调整),这是不希望的。

同时,您从老板那里听到您可能必须添加一项功能,其中每个控制器在启动时都需要通过Internet从公司托管的驱动程序存储库中检查驱动程序的最新版本。

现在您开始考虑要添加的新功能与“重置”功能类似,如果不重构应用程序,则会增加应用程序问题(V2)。

您开始考虑使用继承,以便可以利用JAVA的多态功能,并向其中添加一个新的抽象类(ControllerV3)。

  1. 声明get和set方法的签名。
  2. 包含调整方法的实现,该实现先前已在所有控制器之间复制。
  3. 声明setDefault方法,以便可以利用多态性轻松实现重置功能。

有了这些改进,您就可以使用电视应用程序的第3版。

应用版本3

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

abstract class ControllerV3 {
    abstract void set(int value);
    abstract int get();
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
    abstract void setDefault();
}
class VolumeControllerV3 extends ControllerV3   {

    private int defaultValue = 25;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
}
class  BrightnessControllerV3  extends ControllerV3   {

    private int defaultValue = 50;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
}
class ColourControllerV3 extends ControllerV3   {

    private int defaultValue = 40;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
}

class ResetFunctionV3 {

    private List<ControllerV3> controllers = null;

    ResetFunctionV3(List<ControllerV3> controllers)  {
        this.controllers = controllers;
    }
    void onReset()    {
        for (ControllerV3 controllerV3 :this.controllers)  {
            controllerV3.setDefault();
        }
    }
}
/*
 *       so on
 *       There can be n number of controllers
 *
 * */
public class TvApplicationV3 {
    public static void main(String[] args)  {
        VolumeControllerV3 volumeControllerV3 = new VolumeControllerV3();
        BrightnessControllerV3 brightnessControllerV3 = new BrightnessControllerV3();
        ColourControllerV3 colourControllerV3 = new ColourControllerV3();

        List<ControllerV3> controllerV3s = new ArrayList<>();
        controllerV3s.add(volumeControllerV3);
        controllerV3s.add(brightnessControllerV3);
        controllerV3s.add(colourControllerV3);

        ResetFunctionV3 resetFunctionV3 = new ResetFunctionV3(controllerV3s);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV3.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV3.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV3.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV3.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV3.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV3.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV3.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

尽管解决了V2问题列表中列出的大多数问题,但

电视应用程序版本3中的问题

  1. 重置要素类(ResetFunctionV3)可以访问Controller类的其他方法(调整),这是不希望的。

同样,您考虑解决此问题,因为现在您还需要实现另一个功能(启动时更新驱动程序)。如果您不修复它,它也会被复制到新功能中。

因此,您可以划分抽象类中定义的协定,并为

  1. 重置功能。
  2. 驱动程序更新。

并让您的第一个具体课程实施如下

应用版本4

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

interface OnReset {
    void setDefault();
}
interface OnStart {
    void checkForDriverUpdate();
}
abstract class ControllerV4 implements OnReset,OnStart {
    abstract void set(int value);
    abstract int get();
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

class VolumeControllerV4 extends ControllerV4 {

    private int defaultValue = 25;
    private int value;
    @Override
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for VolumeController .... Done");
    }
}
class  BrightnessControllerV4 extends ControllerV4 {

    private int defaultValue = 50;
    private int value;
    @Override
    int get()    {
        return value;
    }
    @Override
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }

    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for BrightnessController .... Done");
    }
}
class ColourControllerV4 extends ControllerV4 {

    private int defaultValue = 40;
    private int value;
    @Override
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for ColourController .... Done");
    }
}
class ResetFunctionV4 {

    private List<OnReset> controllers = null;

    ResetFunctionV4(List<OnReset> controllers)  {
        this.controllers = controllers;
    }
    void onReset()    {
        for (OnReset onreset :this.controllers)  {
            onreset.setDefault();
        }
    }
}
class InitializeDeviceV4 {

    private List<OnStart> controllers = null;

    InitializeDeviceV4(List<OnStart> controllers)  {
        this.controllers = controllers;
    }
    void initialize()    {
        for (OnStart onStart :this.controllers)  {
            onStart.checkForDriverUpdate();
        }
    }
}
/*
*       so on
*       There can be n number of controllers
*
* */
public class TvApplicationV4 {
    public static void main(String[] args)  {
        VolumeControllerV4 volumeControllerV4 = new VolumeControllerV4();
        BrightnessControllerV4 brightnessControllerV4 = new BrightnessControllerV4();
        ColourControllerV4 colourControllerV4 = new ColourControllerV4();
        List<ControllerV4> controllerV4s = new ArrayList<>();
        controllerV4s.add(brightnessControllerV4);
        controllerV4s.add(volumeControllerV4);
        controllerV4s.add(colourControllerV4);

        List<OnStart> controllersToInitialize = new ArrayList<>();
        controllersToInitialize.addAll(controllerV4s);
        InitializeDeviceV4 initializeDeviceV4 = new InitializeDeviceV4(controllersToInitialize);
        initializeDeviceV4.initialize();

        List<OnReset> controllersToReset = new ArrayList<>();
        controllersToReset.addAll(controllerV4s);
        ResetFunctionV4 resetFunctionV4 = new ResetFunctionV4(controllersToReset);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV4.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV4.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV4.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV4.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV4.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV4.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV4.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

现在,您所面对的所有问题都得到解决,并且您意识到使用继承和多态性可以

  1. 使应用程序的各个部分保持松散耦合。(不需要使重置或驱动程序更新功能组件知道实际的控制器类(音量,亮度和颜色),任何实现OnReset或OnStart的类都可以被重置或驱动程序更新功能接受组件)。
  2. 应用程序增强变得更容易。(新添加的控制器不会影响重置或驱动程序更新功能组件,现在,添加新组件确实非常容易)
  3. 保留抽象层。(现在,“重置”功能只能看到控制器的setDefault方法,而“重置”功能只能看到控制器的checkForDriverUpdate方法)

希望这可以帮助 :-)


3

重载一词是指具有相同名称的某些事物的多个版本,通常是具有不同参数列表的方法

public int DoSomething(int objectId) { ... }
public int DoSomething(string objectName) { ... }

因此,这些功能可能会执行相同的操作,但是您可以选择使用ID或名称来调用它。与继承,抽象类等无关。

如您在问题中所述,重载通常是指多态性


2

重载是当您定义两个具有相同名称但参数不同的方法时

覆盖是通过子类中具有相同名称的函数更改基类的行为的地方。

因此,多态与覆盖有关,但实际上与过载无关。

但是,如果有人对“什么是多态性”这个问题给了我一个简单的“覆盖”答案。我要求进一步解释。


2

覆盖更像是通过声明与上层方法(super方法)具有相同名称和签名的方法来隐藏继承的方法,这为类增加了多态行为。换句话说,将在运行时而不是在编译时做出选择将被调用的方法的决定。这导致了接口和实现的概念。


2

什么是多态性?

来自Java 教程

字典中的多态性定义是指生物学中的一种原理,其中生物或物种可以具有许多不同的形式或阶段。该原理也可以应用于面向对象的编程和Java语言之类的语言。一个类的子类可以定义自己的独特行为,但可以共享父类的某些相同功能。

通过考虑示例和定义,覆盖应该接受。

关于第二个查询:

如果您有一个抽象基类,它定义了一个没有实现的方法,并且您在子类中定义了该方法,那该方法是否仍然覆盖?

它应该被称为覆盖。

看一下这个例子,以了解不同类型的覆盖。

  1. 基类不提供任何实现,子类必须重写完整的方法-(抽象)
  2. 基类提供默认实现,子类可以更改行为
  3. 子类通过调用将扩展添加到基类实现中 super.methodName()作为第一条语句,
  4. 基类定义算法的结构(模板方法),子类将覆盖算法的一部分

代码段:

import java.util.HashMap;

abstract class Game implements Runnable{

    protected boolean runGame = true;
    protected Player player1 = null;
    protected Player player2 = null;
    protected Player currentPlayer = null;

    public Game(){
        player1 = new Player("Player 1");
        player2 = new Player("Player 2");
        currentPlayer = player1;
        initializeGame();
    }

    /* Type 1: Let subclass define own implementation. Base class defines abstract method to force
        sub-classes to define implementation    
    */

    protected abstract void initializeGame();

    /* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
    protected void logTimeBetweenMoves(Player player){
        System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
    }

    /* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
        super.methodName() in first line of the child class method and specific implementation later */
    protected void logGameStatistics(){
        System.out.println("Base class: logGameStatistics:");
    }
    /* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
    protected void runGame() throws Exception{
        System.out.println("Base class: Defining the flow for Game:");  
        while ( runGame) {
            /*
            1. Set current player
            2. Get Player Move
            */
            validatePlayerMove(currentPlayer);  
            logTimeBetweenMoves(currentPlayer);
            Thread.sleep(500);
            setNextPlayer();
        }
        logGameStatistics();
    }
    /* sub-part of the template method, which define child class behaviour */
    protected abstract void validatePlayerMove(Player p);

    protected void setRunGame(boolean status){
        this.runGame = status;
    }
    public void setCurrentPlayer(Player p){
        this.currentPlayer = p;
    }
    public void setNextPlayer(){
        if ( currentPlayer == player1) {
            currentPlayer = player2;
        }else{
            currentPlayer = player1;
        }
    }
    public void run(){
        try{
            runGame();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

class Player{
    String name;
    Player(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

/* Concrete Game implementation  */
class Chess extends Game{
    public Chess(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized Chess game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate Chess move:"+p.getName());
    }
    protected void logGameStatistics(){
        super.logGameStatistics();
        System.out.println("Child class: Add Chess specific logGameStatistics:");
    }
}
class TicTacToe extends Game{
    public TicTacToe(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized TicTacToe game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate TicTacToe move:"+p.getName());
    }
}

public class Polymorphism{
    public static void main(String args[]){
        try{

            Game game = new Chess();
            Thread t1 = new Thread(game);
            t1.start();
            Thread.sleep(1000);
            game.setRunGame(false);
            Thread.sleep(1000);

            game = new TicTacToe();
            Thread t2 = new Thread(game);
            t2.start();
            Thread.sleep(1000);
            game.setRunGame(false);

        }catch(Exception err){
            err.printStackTrace();
        }       
    }
}

输出:

Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:
Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:

2

我想你们正在混合概念。多态是对象在运行时行为不同的能力。为此,您需要满足两个条件:

  1. 后期绑定
  2. 遗产。

话虽如此,根据您使用的语言,重载意味着不同于覆盖。例如,在Java中,不存在重载而是重载。子类中提供了对其基类具有不同签名的重载方法。否则它们将被覆盖(请注意,我的意思是现在无法从对象外部调用基类方法)。

但是在C ++中并非如此。任何重载方法(无论签名是否相同(不同数量,不同类型))都将被覆盖。直到今天,很明显,从子类对象外部调用基类的方法时,该方法在子类中不再可用。

所以答案是在谈论Java使用重载时。任何其他语言都可能与c ++中的情况有所不同


1

就其含义而言,多态性更有可能...在Java中覆盖

这都是关于SAME对象在不同情况下的不同行为(以编程方式...您可以调用不同的ARGUMENTS)

我认为以下示例将帮助您理解...尽管不是纯Java代码...

     public void See(Friend)
     {
        System.out.println("Talk");
     }

但是,如果我们更改ARGUMENT ...的行为将被更改...

     public void See(Enemy)
     {
        System.out.println("Run");
     }

人员(此处为“对象”)是相同的...


1

多态性是对象的多种实现,或者您可以说对象的多种形式。可以说您有一个类Animals作为抽象基类,并且它具有称为movement()的方法,该方法定义动物的移动方式。现在,现实中我们有不同种类的动物,它们的移动方式也不同,其中一些动物有2条腿,其他动物有4条腿,有些没有腿,等等。为了定义movement()地球上每种动物的不同之处,我们需要应用多态性。但是,您需要定义更多的类,例如class Dogs Cats Fish等。然后您需要从基类扩展这些类Animals并覆盖其方法movement()根据所拥有的每种动物使用新的移动功能。您也可以使用Interfaces实现这一目标。这里的关键字是重载,重载是不同的,不被视为多态。通过重载,您可以在同一对象或类上定义多个“具有相同名称”但具有不同参数的方法。


0

多态性与语言使用单一接口统一对待不同对象的能力有关。因为它与覆盖相关,所以接口(或基类)是多态的,实现者是覆盖的对象(同一枚奖牌的两个面)

无论如何,可以使用其他语言(例如c ++)更好地解释这两个术语之间的区别:如果基本函数是虚拟的,则c ++中的多态对象将作为java的对应对象,但是如果方法不是虚拟的,则静态地解决代码跳转,并且在运行时不检查真实类型,因此,多态性包括对象根据其访问接口的不同而具有不同的行为;让我用伪代码举例:

class animal {
    public void makeRumor(){
        print("thump");
    }
}
class dog extends animal {
    public void makeRumor(){
        print("woff");
    }
}

animal a = new dog();
dog b = new dog();

a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

(假设makeRumor不是虚拟的)

Java并没有真正提供这种级别的多态性(也称为对象切片)。

动物a = new dog(); 狗b =新的dog();

a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

在这两种情况下,它只会打印woff ..因为a和b指的是dog类



动物a = new dog(); 一个被构造成狗,并会打印“ woff”。如果您要打印重击,则需要进行上投。((animal)a).makeRumor()
Chris Cudmore

那是参考向上投射,但是对象仍然是一条狗。如果要使其成为动物,则必须显式上射该对象。
克里斯·库德莫

弄清楚了。问题被标记为Java。您回答了C ++。您可能在C ++中是正确的。我在Java中绝对正确。
克里斯·库德莫

每次涉及到复制构造函数时,都应该在这里发生。这是一个参考fredosaurus.com/notes-cpp/oop-condestructors / ... 情况三匹配;忽略新的运算符,该运算符只能消除歧义。
洛伦佐·博卡恰

0
import java.io.IOException;

class Super {

    protected Super getClassName(Super s) throws IOException {
        System.out.println(this.getClass().getSimpleName() + " - I'm parent");
        return null;
    }

}

class SubOne extends Super {

    @Override
    protected Super getClassName(Super s)  {
        System.out.println(this.getClass().getSimpleName() + " - I'm Perfect Overriding");
        return null;
    }

}

class SubTwo extends Super {

    @Override
    protected Super getClassName(Super s) throws NullPointerException {
        System.out.println(this.getClass().getSimpleName() + " - I'm Overriding and Throwing Runtime Exception");
        return null;
    }

}

class SubThree extends Super {

    @Override
    protected SubThree getClassName(Super s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Returning SubClass Type");
        return null;
    }

}

class SubFour extends Super {

    @Override
    protected Super getClassName(Super s) throws IOException {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Throwing Narrower Exception ");
        return null;
    }

}

class SubFive extends Super {

    @Override
    public Super getClassName(Super s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and have broader Access ");
        return null;
    }

}

class SubSix extends Super {

    public Super getClassName(Super s, String ol) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading ");
        return null;
    }

}

class SubSeven extends Super {

    public Super getClassName(SubSeven s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading because Method signature (Argument) changed.");
        return null;
    }

}

public class Test{

    public static void main(String[] args) throws Exception {

        System.out.println("Overriding\n");

        Super s1 = new SubOne(); s1.getClassName(null);

        Super s2 = new SubTwo(); s2.getClassName(null);

        Super s3 = new SubThree(); s3.getClassName(null);

        Super s4 = new SubFour(); s4.getClassName(null);

        Super s5 = new SubFive(); s5.getClassName(null);

        System.out.println("Overloading\n");

        SubSix s6 = new SubSix(); s6.getClassName(null, null);

        s6 = new SubSix(); s6.getClassName(null);

        SubSeven s7 = new SubSeven(); s7.getClassName(s7);

        s7 = new SubSeven(); s7.getClassName(new Super());

    }
}
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.