приведение объекта подкласса к суперклассу

У меня есть вопрос о upcast/downcast.

Я создал абстрактный суперкласс Animal, подкласс Dog и подкласс BigDog. и я также даю абстрактный метод в Animal и переопределяю его в Dog и BigDog.

abstract public class Animal {
    abstract public void greeting();
}

public class Dog extends Animal {
   @Override
   public void greeting() {
      System.out.println("Woof!");
   }
}

public class BigDog extends Dog {
   @Override
   public void greeting() {
      System.out.println("Woow!");
   }
}

теперь мой тестовый код:

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

      Animal animal2 = new Dog();
      Animal animal3 = new BigDog();

      // Downcast
      Dog dog2 = (Dog) animal2;               //cast Animal class to Dog class, legit
      BigDog bigDog2 = (BigDog) animal3;      //cast Animal to BigDog, legit;
      Dog dog3 = (Dog) animal3;               //Animal Class contains BigDog cast into Dog?
      dog2.greeting();
      dog3.greeting();                    //in which class the method is called?
   }
}

Я понимаю взаимосвязь между суперклассом/подклассом и тем, как работает приведение. Однако мой вопрос заключается в том, можете ли вы преобразовать суперкласс в конкретный подкласс, зная, что между ними есть класс? например, если у меня есть объект класса Animal, содержащий объект BigDog, могу ли я преобразовать объект в Dog? что, если в BigDog есть методы, которых нет в Dog?

Короче говоря, вы, безусловно, можете сказать, что объект суперкласса является объектом подкласса, но почему вы можете инвертировать?


На второй мысли,

Я предполагаю следующее: я прошу JVM привести ссылку класса Animal к Dog и связать новую ссылку Dog с объектом BigDog, а не действительно привести объект BigDog.

Таким образом, я могу вызвать все методы Dog и Animal для этой ссылки Dog (к BigDog), но ни один из методов BigDog, если только он не был переопределен в BigDog.

Что Java проверяет при вызове метода: имеет ли ссылка (DOG) ссылку и имеет ли объект (BigDog) переопределение. если нет, то вызывается метод Dog, иначе вызывается метод BigDog.

Кто-нибудь может подтвердить мою догадку?


person sygede    schedule 30.08.2013    source источник
comment
Правильное использование методов: dog2.greeting(); и dog3.greeting(); или добавить метод public void greeting(Animal animal);. Затем исходный код будет скомпилирован.   -  person Pawel    schedule 30.08.2013
comment
извините, ребята, скопируйте и вставьте, забыл изменить параметры.   -  person sygede    schedule 31.08.2013
comment
Это хороший пример статического и динамического типа переменной. Для этого есть несколько книг/тредов   -  person Stefan Warminski    schedule 21.03.2018


Ответы (4)


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

Лучший способ привести к подклассу — проверить, можно ли это сделать:

  if ( doggy instanceof BigDog ) {
      doSomethingWithBigdog( (BigDog) doggy );
  } else if ( doggy instanceof SmallDog ) {
      doSomethingWithSmalldog( (SmallDog) doggy );     
  } else {
     // Neither a big dog nor a small dog
  }

  ...

  private void doSomethingWithBigdog( BigDog dog ) {
    ...
  }

  private void doSomethingWithSmalldog( SmallDog dog ) {
    ...
  }

Имейте в виду, что кастинг — это зло. Иногда это необходимо, но часто (не всегда) его можно спроектировать, реализуя методы базового класса или не назначая собаку переменной животного, а оставляя ее собакой.

person extraneon    schedule 30.08.2013

If I have an Animal class object contains a BigDog object, can I cast the object to Dog? what if there are methods in BigDog that do not exist in Dog?.

Просто вы получите ошибку компилятора. Поскольку вы не можете вызвать метод, который не объявлен в родительском и объявлен в дочернем классе, используя родительскую ссылку

person Prabhaker A    schedule 30.08.2013

Не существует метода, сигнатура которого будет соответствовать этим вызовам методов:

dog2.greeting(dog3);
dog3.greeting(dog2); 

Итак, это в значительной степени ошибка компиляции.

Вам нужно знать о диспетчеризации динамических методов.

вот несколько ссылок 1,2,3 просмотрите их.

person codeMan    schedule 30.08.2013
comment
моя ошибка при копировании и вставке. исходный код имеет методы. - person sygede; 31.08.2013
comment
Ok. Пройдитесь по ссылкам, которые есть в ответе. Приветствие(), из которого когда-либо будет вызываться объект, на который ссылается ссылка на собаку. - person codeMan; 02.09.2013

Сначала исправьте исходный код, чтобы он скомпилировался. Правильное использование методов: dog2.greeting(); и dog3.greeting(); или добавить метод public void greeting(Animal animal);.


dog3.greeting(); - вызов метода greeting() для dog3. dog3 имеет ту же ссылку, что и animal3. animal3 имеет ссылку на BigDog, поэтому метод greeting() вызывается для класса BigDog, и на выходе получается Woow!


Когда вы наследуете Dog от класса Animal, тогда класс Dog имеет все методы из класса Animal.

person Pawel    schedule 30.08.2013