【问题标题】:Polymorphism vs Overriding vs Overloading多态性 vs 覆盖 vs 重载
【发布时间】:2010-09-14 08:25:51
【问题描述】:

在Java方面,当有人问:

什么是多态性?

重载覆盖是可接受的答案吗?

我认为还有更多的东西。

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

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

【问题讨论】:

标签: java oop polymorphism overloading overriding


【解决方案1】:

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

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

这个类是抽象的,因为 goPee() 方法对于人类来说是不可定义的。它只能为子类 Male 和 Female 定义。此外,人类是一个抽象概念——你不能创造一个既不是男性也不是女性的人。必须是其中之一。

所以我们使用抽象类来推迟实现。

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

【讨论】:

  • @yuudachi。我在上课时想出了这个例子。规范的“银行账户”类并没有真正表达基类的“抽象性”。另一个典型的例子(动物,制造噪音)太抽象而无法理解。我正在寻找一个具有太明显子类的单一基础。实际上,goPee() 是我想出的唯一一个不是性别歧视或刻板印象的例子。 (虽然在课堂上,我打印了“在左边的大厅”而不是站起来或坐下。)
  • 这个例子也很好地突出了使用分层系统描述生物系统的困难。一些人类,例如非常年轻的人,几乎可以在任何位置小便——而婴儿不能轻易被告知 goPee()。有些人是双性人,“男性”或“女性”的生物学标签变得相当模糊;社会意义更加复杂。作为一个教学示例,它展示了建模假设如何产生负面结果,例如暗示失禁或双性人的某人(例如,OO 编程的学生)实际上不是人类。
  • 我能想到至少有几个人会反驳你的“你不能创造一个既不是男性也不是女性的人”的论点,尽管它仍然适用于你的代码......我想我说的抽象不好? ;)
  • 我认为重要的是要指出它只是多态性,因为只能在运行时确定调用哪个版本的 goPee()。虽然这个例子暗示了这一点,但很高兴指出为什么这就是多态性。此外,它不需要兄弟类。也可以是亲子关系。甚至完全不相关的类恰好具有相同的功能。 .toString() 函数就是一个例子。可以在任何对象上随机调用,但编译器永远无法确切知道是哪种类型的对象。
  • @AndrewDalke,关于生物复杂性的注释 +1。此外,goPee 不会将引力场作为输入。这种对全局状态的依赖使CatheterizedIntersexAstronaut 的单元测试变得困难,并表明子类化可能并不总是组合特征的最佳方法。
【解决方案2】:

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

覆盖是一种发生在从另一个类继承的类中的函数。覆盖函数“替换”从基类继承的函数,但这样做的方式是,即使其类的实例通过多态性伪装成不同的类型,也会调用它。参考前面的示例,您可以定义自己的类并覆盖 toString() 函数。因为这个函数是从 Object 继承的,所以如果你把这个类的一个实例复制到一个 Object 类型的变量中,它仍然是可用的。通常,如果您在类伪装成 Object 时调用 toString(),则实际触发的 toString 版本是在 Object 本身上定义的版本。但是,由于该函数是一个覆盖函数,因此即使类实例的真实类型隐藏在多态性后面,也会使用类中 toString() 的定义。

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

【讨论】:

  • 这是旧的,但多态并不意味着另一个类必须在继承树中。如果你认为接口是继承树的一部分,它在 Java 中会这样做,但在 Go 中不会,因为接口是隐式实现的。
  • 其实你根本不需要多态类。
  • 我是新手,如果我错了,请纠正我,但我不会说重载与多态无关。至少在Java中,多态是根据调用者的类型来选择实现,而重载是根据参数的类型来选择实现,不是吗?看到两者之间的相似性有助于我理解它。
  • 不正确。 Ad hoc polymorphism 是您在 重载 部分中描述的内容,并且 一种多态性的情况。
  • “它与覆盖或多态无关”。这种说法是错误的。
【解决方案3】:

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

多态可以通过两种方式实现,分别是

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

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

方法覆盖表示我们在不同的类中使用方法名,即在子类中使用父类方法。

在Java中实现多态一个超类引用变量可以持有子类对象。

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

【讨论】:

  • +1 得到很好的答案。接受的答案仅解释了一种类型的多态性。这个答案是完整的。
  • 多态是一种范式 (OOP),但重写和重载是语言工具。
  • 多态也可以通过泛型类型来实现。
【解决方案4】:

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

你可以在一个类中有一个方法 被覆盖在一个或 更多的子类。该方法确实 不同的事情取决于哪个 类用于实例化一个对象。

    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);
        }
    }

【讨论】:

  • 我想它被否决了,因为历史上方法重载不被视为面向对象范式中多态性的一部分。方法重载和多态是编程语言的两个正交、独立的特征。
  • 正如我在这里的回答中所说,我不同意——这两个特征不是正交的,而是密切相关的。多态性!=继承。你有我的赞成票。
  • 换句话说,类型多态性与临时多态性。我赞成这个答案,即使它没有应有的完整,因为它正确地指出重载和覆盖都与多态性有关。说 OOP 语言中的多态性只能通过类继承来实现是完全错误的——我们应该记住,除了 Java 和 C++ 之外,还有一些其他的 OOP 语言可以使用多重调度、ad hoc 多态性、参数多态性等概念.
  • @rsenna 这可能不完整,但它比其他恕我直言更好地回答了这个问题。另外,非常高兴您提到了临时和参数多态性。
【解决方案5】:

这是伪 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() 方法的特定实现行为。

编辑:看起来布莱恩打败了我。有趣的是,我们使用了相同的示例。但是上面的代码应该有助于澄清概念。

【讨论】:

  • 这是一个运行时多态的例子。编译时多态性也可以通过方法重载和泛型类型实现。
  • 形状 -> 平行四边形 -> 矩形 -> 正方形
  • @yankee2905 在这种情况下,我认为你可以使用接口,因为一个类可以实现多个接口。
  • @Zhisheng 或者在抽象父类中添加一个pee方法?我会使用接口来实现其他东西。
【解决方案6】:

你说得对,重载不是答案。

两者都不是压倒一切的。覆盖是获得多态性的方法。多态性是对象根据其类型改变行为的能力。当表现出多态性的对象的调用者不知道该对象是什么特定类型时,这一点得到了最好的证明。

【讨论】:

  • 改变的应该不是对象的行为,而是他的实现。相同的行为,不同的实现,这就是多态性。
  • @QBziZ 你需要定义behaviour,尤其是形容词same。如果行为相同,为什么它们的实现应该不同?并不是有人对某个实现不满意,因此需要一个不同的实现。
【解决方案7】:

具体说重载或覆盖并不能给出完整的画面。多态性只是对象根据其类型专门化其行为的能力。

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

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

【讨论】:

  • 就所涉及的词的一般含义而言,您是对的,但在编程环境中,当人们说“多态性”时,他们总是指“基于继承的多态性”。有趣的一点,但我认为这样描述多态性会让人们感到困惑。
  • 仅从继承的角度来解释多态性可能更容易一些,但是从提出这个特定问题的方式来看,我认为描述参数多态性也是谨慎的做法。
  • 为了清楚起见,我认为应该说明不同的形式——我什至没有充分地做到这一点——因为这里有一些答案是绝对的。我恭敬地不同意在“程序员上下文中......'多态'总是意味着'基于继承的多态'”
  • 我认为重载最好归类为 Ad-hoc_polymorphism en.wikipedia.org/wiki/…
  • 我倾向于同意“The Digital Gabeg”的关注。如果你在讨论 OOP,多态通常意味着子类型多态,如果你在讨论类型论,它意味着任何类型的多态。但是就像你说的那样,对于“程序员上下文”来说,它太模棱两可了。
【解决方案8】:

多态只是意味着“多种形式”。

它不需要继承来实现...作为接口实现,它根本不是继承,服务于多态需求。可以说,接口实现比继承“更好”地服务于多态需求。

例如,你会创建一个超类来描述所有可以飞行的东西吗?我应该不这么认为。您最好创建一个描述飞行的界面并保留它。

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

【讨论】:

  • 绝对是最好的答案。多态性可以应用于所有语言结构,无论是名词(类)还是动词(方法)。
【解决方案9】:

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

调用代码不必知道它们是什么具体的动物。

这就是我认为的多态性。

【讨论】:

    【解决方案10】:

    虽然多态性已经在这篇文章中进行了详细的解释,但我想更加强调为什么它是其中的一部分。

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

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

    假设您是一家电视公司的软件工程师,您被要求为音量、亮度和颜色控制器编写软件,以根据用户命令增加和减少它们的值。

    您首先通过添加来为这些功能中的每一个编写类

    1. set:- 设置控制器的值。(假设这有控制器特定的代码)
    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;
            }
    
        }
        }
    }
    

    现在您已经准备好部署我们的第一个工作应用程序版本。是时候分析目前所做的工作了。

    TV 应用程序版本 1 中的问题

    1. Adjust(int value) 代码在所有三个类中都是重复的。您希望最小化代码重复性。 (但您没有想到通用代码并将其移至某个超类以避免重复代码)

    只要您的应用程序按预期工作,您就决定接受它。

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

    您开始为新功能编写一个新类 (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 类 (adjust) 的其他方法,这是不可取的。

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

    现在您开始认为要添加的这个新功能类似于重置功能,如果您不重构应用程序,应用程序问题 (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 的问题列表中列出的大部分问题都已解决,除了

    TV 应用程序版本 3 中的问题

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

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

    所以你划分抽象类中定义的契约,为

    写2个接口
    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 的类都可以接受 Reset 或驱动程序更新功能组件)。
    2. 应用程序增强变得更容易。(新增控制器不会影响重置或驱动程序更新功能组件,现在添加新的非常容易)
    3. 保留抽象层。(现在Reset功能只能看到控制器的setDefault方法,而Reset功能只能看到控制器的checkForDriverUpdate方法)

    希望,这会有所帮助:-)

    【讨论】:

      【解决方案11】:

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

      【讨论】:

        【解决方案12】:

        重载是指定义了两个同名但参数不同的方法

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

        所以多态性与覆盖有关,但与真正的重载无关。

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

        【讨论】:

          【解决方案13】:

          两者都没有:

          重载是指函数名相同但参数不同。

          重写是子类用它自己的方法替换父类的方法(这本身并不构成多态性)。

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

          在 Java 中,您可以通过集合库看到很多多态性:

          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() 来匹配参数。

          【讨论】:

            【解决方案14】:

            什么是多态性?

            来自javatutorial

            多态性的字典定义是指生物学中的一个原则,其中一个有机体或物种可以有许多不同的形式或阶段。这个原则也可以应用于面向对象的编程和像 Java 语言这样的语言。 一个类的子类可以定义它们自己的独特行为,同时共享一些与父类相同的功能。

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

            关于您的第二个查询:

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

            它应该被称为覆盖。

            查看此示例以了解不同类型的覆盖。

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

            代码sn-p:

            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:
            

            【讨论】:

              【解决方案15】:

              术语重载是指具有相同名称的多个版本,通常是具有不同参数列表的方法

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

              因此,这些函数可能会做同样的事情,但您可以选择使用 ID 或名称来调用它。与继承、抽象类等无关。

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

              【讨论】:

                【解决方案16】:

                重写更像是通过声明与上层方法(超级方法)具有相同名称和签名的方法来隐藏继承的方法,这为类添加了多态行为。 换句话说,选择要调用的最高级别方法的决定将在运行时而不是在编译时做出。 这就引出了接口和实现的概念。

                【讨论】:

                  【解决方案17】:

                  多态是对象的多种实现,或者您可以说对象的多种形式。假设您有类Animals 作为抽象基类,它有一个名为movement() 的方法,它定义了动物移动的方式。现在实际上我们有不同种类的动物,它们的移动方式也不同,有些有 2 条腿,有些有 4 条,有些没有腿,等等。要为地球上的每种动物定义不同的movement(),我们需要申请多态性。但是,您需要定义更多类,即类DogsCatsFish 等。然后您需要从基类Animals 扩展这些类,并使用基于每个类的新移动功能覆盖其方法movement()你有的动物。您也可以使用Interfaces 来实现这一点。这里的关键字是覆盖,重载是不同的,不被认为是多态。通过重载,您可以在同一对象或类上定义多个“具有相同名称”但参数不同的方法。

                  【讨论】:

                    【解决方案18】:

                    我认为你们正在混合概念。 多态性是对象在运行时表现不同的能力。为此,您需要两个先决条件:

                    1. 后期装订
                    2. 继承。

                    根据您使用的语言,重载的含义与覆盖不同。例如在Java 中不存在覆盖,而是重载重载 方法与基类不同的签名在子类中可用。否则它们将被覆盖(请注意,我的意思是现在无法从对象外部调用您的基类方法)。

                    但在 C++ 中并非如此。任何重载方法,独立于签名是否相同(不同数量,不同类型)也被覆盖。也就是说,显然,当从子类对象外部调用时,基类的方法在子类中不再可用。

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

                    【讨论】:

                      【解决方案19】:

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

                      这都是关于同一对象在不同情况下的不同行为(在编程方式中......你可以调用不同的参数)

                      我认为下面的例子会帮助你理解......虽然它不是纯java代码......

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

                      但是如果我们改变 ARGUMENT ... BEHAVIOR 将会改变 ...

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

                      人(这里是“对象”)是一样的......

                      【讨论】:

                        【解决方案20】:

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

                        无论如何,这两个术语之间的区别最好使用其他语言来解释,例如 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 = 新狗();

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

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

                        【讨论】:

                        • 动物 a = new dog(); a 被构造为狗,并将打印“woff”。如果你想让它打印重击,那么你需要向上转换它。((animal) a).makeRumor()
                        • 这是引用向上转换,但对象仍然是一条狗。如果你想让它成为动物,你必须明确地向上转换对象。
                        • 想通了。问题被标记为 Java。你回答了 C++。您在 C++ 中可能是正确的。我在 Java 中绝对是正确的。
                        • 应该在每次涉及复制构造函数时发生这里是参考fredosaurus.com/notes-cpp/oop-condestructors/… 案例三匹配;忽略 new 运算符,它只是为了消除创建歧义。
                        【解决方案21】:
                        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());
                        
                            }
                        }
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-06-26
                          • 1970-01-01
                          • 2015-08-31
                          • 2011-04-19
                          • 2012-01-03
                          相关资源
                          最近更新 更多