Почему повышение класса не меняет переопределенные методы?

У меня есть подкласс 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()?


person TheValars    schedule 15.12.2015    source источник


Ответы (5)


Пожалуйста, взгляните на этот вопрос для большего понимания upcast и downcast:

В чем разница между повышением и понижением по отношению к переменной класса

Я также сделал пример для просмотра поведения upcast:

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 это описано здесь "Сужение преобразования ссылок", как следует из его имени, сужается или расширяется только ссылка (или повышается или понижается), а не экземпляр.

person cpz    schedule 15.12.2015
comment
Я изучаю эту концепцию приведения типов и не могу понять, что вы изменили объект родительского класса (животное) на объект дочернего класса (собака) в методе upcastToDog, но вы вызываете его повышением. Почему? Разве приведение от родительского к дочернему классу не называется downcasting? Извините, если я ошибаюсь. - person Shashank Gupta; 29.07.2020

Объект, для которого вы на самом деле вызываете метод, принадлежит ScottishPerson, поэтому во время компиляции он проверяет ссылочную переменную, но во время выполнения он всегда выполняет методы, принадлежащие объекту, а не ссылаться на него. На самом деле за этой концепцией стоит полиморфизм времени выполнения.

person Gaurav Jeswani    schedule 15.12.2015
comment
Но почему поле name указывает на поле BritishPerson. - person Codebender; 15.12.2015
comment
Полиморфизм — это концепция методов, а не переменных. - person Gaurav Jeswani; 15.12.2015

  • Доступ к статическим полям, полям экземпляров и статическим методам зависит от класса ссылочной переменной, а не фактического объекта, на который указывает переменная.
  • Помните, что переменные-члены затенены, а не переопределены.
  • Это противоположно тому, что происходит в случае методов экземпляра.
    В случае методов экземпляра вызывается метод фактического класса объекта.

Рассмотрим следующий пример.

    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
    }

В вашем сценарии свойство name объекта britishPersonUpcasted затенено свойством BritishPerson.

Надеюсь это поможет.

person tharindu_DG    schedule 15.12.2015

Переменные не полиморфны в 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);
  }

}
person Joydip Datta    schedule 15.12.2015

Вы можете объявить поле в подклассе с тем же именем, что и в суперклассе, тем самым скрыв его (не рекомендуется).

даже если вы этого хотите, вы можете получить к нему доступ по ключевому слову super

person Girdhar Singh Rathore    schedule 15.12.2015