【问题标题】:Why is upcasting a Class not changing overridden methods? [duplicate]为什么向上转换一个类不会改变重写的方法? [复制]
【发布时间】:2016-03-20 19:03:44
【问题描述】:

我有一个子类 ScottishPerson,它继承自类 BritishPerson

class BritishPerson {
    public String name = "A british name";

    public void salute() {
        System.out.println("Good Morning!");
    }
}

class ScottishPerson extends BritishPerson {
    public String name = "A scottish name "; //Variable overriding
    public String clanName = "MacDonald";

    public void salute() //Method overriding
    {
        System.out.println("Madainn Mhath!");
    }

    public void warcry() {
        System.out.println("Alba Gu Brath!");
    }
}

class Driver {

    public static void main(String[] args) {
        ScottishPerson scottishPerson = new ScottishPerson(); //Created as a subclass, can always be upcasted.
        BritishPerson britishPerson = new BritishPerson(); //Created as the superclass, throws an error when downcasted.
        BritishPerson britishPersonUpcasted =
                new ScottishPerson(); //Created as the subclass but automatically upcasted, can be downcasted again.

        //Checking the methods and parameters of scottishPerson
        scottishPerson.salute();
        scottishPerson.warcry();
        System.out.println(scottishPerson.name);
        System.out.println(scottishPerson.clanName);

        //Checking the methods and parameters of britishPerson
        britishPerson.salute();
        System.out.println(britishPerson.name);

        //Checking the methods and parameters of britishPersonUpcasted
        britishPersonUpcasted.salute();
        System.out.println(britishPersonUpcasted.name);
    }
}

运行代码,这是输出。

Madainn Mhath!
Alba Gu Brath!
A scottish name 
MacDonald
Good Morning!
A british name
Madainn Mhath!
A british name

这就是混乱所在。将ScottishPerson 向上转换为BritishPerson 会将变量名称更改为在超类中定义的名称。只存在于子类中的方法和变量,例如warcry()clanName 将被丢弃。但是,在向上转换的类上调用方法salute() 仍然会返回基于子类实现的字符串。

是不是因为当我创建对象britishPerson 时,我只初始化BritishPerson 类,而当我创建对象britishPersonUpcasted 时,我同时创建了BritishPerson 类和ScottishPerson 类,这导致了永久覆盖salute() 方法?

【问题讨论】:

    标签: java inheritance subclass superclass upcasting


    【解决方案1】:

    您实际调用方法的对象属于ScottishPerson,因此在编译时它会检查引用变量,但在运行时它总是执行属于该对象的方法而不是引用持有它的对象。运行时多态性实际上隐藏在这个概念的背后。

    【讨论】:

    • 但是为什么name这个字段指向BritishPerson
    • 多态是方法的概念,而不是变量的概念。
    【解决方案2】:

    请查看此问题以进一步了解 upcast 和 downcast:

    What is the difference between up-casting and down-casting with respect to class variable

    我还举了一个例子来观察向上行为:

    abstract class Animal 
    { 
        public void saySomething()
        {
            System.out.println("Some Animal sound");
        }
    
        public abstract void getTheBall();
    }
    
    class Horse extends Animal
    { 
        public void saySomething()
        {
            System.out.println("Neigh Neigh");
        }
    
        public void getTheBall()
        {
            System.out.println("I won't, Try a dog, I am a Horse!");
        }
    }
    
    class Dog extends Animal 
    { 
        public void saySomething()
        {
            System.out.println("woof woof, waon waon");
        }
    
        public void getTheBall()
        {
            System.out.println("huf huf, here it is!");
        }
    }
    
    public class Main 
    {
        public static void main (String [] args) 
        {
            Dog dog = new Dog(); 
            Horse horse = new Horse();
            Animal animal = dog;
            Animal horseAnimal = new Horse();
    
            //upcasting
            Dog upcastedAnimal = upcastToDog(animal);
            dog.saySomething();
            dog.getTheBall();
    
            upcastedAnimal.saySomething();
            upcastedAnimal.getTheBall();
    
            horse.saySomething();
            horse.getTheBall();
    
            try {
                Dog upcastedDog = upcastToDog(horseAnimal);
            } catch (Exception ex){
                System.out.println(ex.getClass().getSimpleName() + ": Obviously a horse is not a dog!");
            }
        }
    
        public static Dog upcastToDog(Animal animal){
            return (Dog) animal;
        }
    }
    

    输出:

    woof woof, waon waon
    huf huf, here it is!
    woof woof, waon waon
    huf huf, here it is!
    Neigh Neigh
    I won't, Try a dog, I am a Horse!
    ClassCastException: Obviously a horse is not a dog!
    

    首先,如果尝试强制转换不兼容的类型,java 将抛出异常。

    在可以进行强制转换的情况下,将始终从实际实例调用被覆盖的方法。在您的情况下,实例是 ScottishPerson,因此将在 ScottishPerson 上调用方法,即使您将其引用保存在 BritishPerson 中也是如此。

    你可以在这里运行示例https://repl.it/B83f/3

    在 JLS 中,这里涵盖了“Narrowing Reference Conversion”,正如他的名字所暗示的,只有引用被缩小或扩大(或向上或向下)而不是实例。

    【讨论】:

    • 我正在学习这种类型转换的概念,我无法理解您在 upcastToDog 方法中将父类对象(Animal)更改为子类对象(Dog),但您正在调用它上扬。为什么?不是从名为downcasting 的父类转换为子类吗?对不起,如果我错了。
    【解决方案3】:
    • 对静态字段、实例字段和静态方法的访问取决于引用变量的类,而不是变量指向的实际对象。
    • 请记住,成员变量是隐藏的,而不是被覆盖的。
    • 这与实例方法的情况相反。
      如果是实例方法,则调用对象的实际类的方法。

    考虑以下示例。

        class ABCD {
            int x = 10;
            static int y = 20;
    
            public String getName() {
                return "ABCD";
            }
        }
    
        class MNOP extends ABCD {
            int x = 30;
            static int y = 40;
    
            public String getName() {
                return "MNOP";
            }
        }
    
        public static void main(String[] args) {
    
          System.out.println(new MNOP().x + ", " + new MNOP().y);
    
          ABCD a = new MNOP();
          System.out.println(a.x); // 10
          System.out.println(a.y); // 20
          System.out.println(a.getName()); // MNOP
        }
    

    在您的场景中,britishPersonUpcasted 对象的name 属性被BritishPerson 遮蔽。

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      变量在 Java 中不是多态的。在子类中声明的相同变量不会覆盖超类中的值。

      要反映超类关联的值,需要将其传递给构造函数,并使用 super 关键字设置超类变量。这样:

        public ScottishPerson(String name) {
          super.name = name;
          this.name = name;
        }
      

      这是您的代码,我已对其进行了修改。

      class BritishPerson
      {
        public String name = "A british name";
      
          public void salute()
          {
              System.out.println("Good Morning!");
          }
      
      }
      
      
      class ScottishPerson extends BritishPerson
      {
        public String name = "A scottish name "; //Variable overriding
        public String clanName = "MacDonald";
      
        public ScottishPerson() {
          // TODO Auto-generated constructor stub
        }
      
        public ScottishPerson(String name) {
          super.name = name;
          this.name = name;
        }
      
          @Override
          public void salute() //Method overriding
          {
              System.out.println("Madainn Mhath!");
          }
      
          public void warcry()
          {
              System.out.println("Alba Gu Brath!");
          }
      
      }
      
      public class Driver {
      
      
      
        public static void main(String[] args) {
          // TODO Auto-generated method stub
      
      
          ScottishPerson scottishPerson = new ScottishPerson(); //Created as a subclass, can always be upcasted.
          BritishPerson britishPerson = new BritishPerson(); //Created as the superclass, throws an error when downcasted.
          BritishPerson britishPersonUpcasted = new ScottishPerson("Another scottish name"); //Created as the subclass but automatically upcasted, can be downcasted again.
      
          //Checking the methods and parameters of scottishPerson
          scottishPerson.salute();
          scottishPerson.warcry();
          System.out.println(scottishPerson.name);
          System.out.println(scottishPerson.clanName);
      
          //Checking the methods and parameters of britishPerson
          britishPerson.salute();
          System.out.println(britishPerson.name);
      
          //Checking the methods and parameters of britishPersonUpcasted
          britishPersonUpcasted.salute();
          System.out.println(britishPersonUpcasted.name);
        }
      
      }
      

      【讨论】:

        【解决方案5】:

        你可以在子类中声明一个与超类同名的字段,从而隐藏它(不推荐)。

        即使你想要它,你也可以通过super关键字访问它

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-02-08
          • 1970-01-01
          • 1970-01-01
          • 2019-07-19
          • 2020-06-14
          • 2017-02-18
          • 2019-09-09
          • 1970-01-01
          相关资源
          最近更新 更多