Можно ли вызывать методы подклассов на объекте суперкласса? - PullRequest
16 голосов
/ 22 мая 2009

Зверь - суперкласс Пса и у собаки есть метод, который называется кора

public void bark()
{
    System.out.println("woof");
}

Примите во внимание следующее:

Animal a = new Dog();
if (a instanceof Dog){
    a.bark();
}

Что будет?

  1. назначение не разрешено
  2. вызов лая разрешен, и во время выполнения выводится «woof»
  3. вызов лая разрешен, но ничего не печатается
  4. вызов bark вызывает ошибку времени компиляции
  5. вызов лая приводит к ошибке времени выполнения

Я сказал 2, когда мы проверяем, является ли объект собакой; поскольку собака - это класс с методом лая, если он есть, мы вызываем его, который выведет: s

Правильно ли мое понимание здесь?

Ответы [ 9 ]

32 голосов
/ 22 мая 2009

Это не скомпилируется, так как у Animal нет метода с именем bark. Думайте об этом так, все собаки - животные, но не все животные - собаки. Все собаки лают, но не все животные лают.

27 голосов
/ 22 мая 2009

нет - ответ:

4) вызов bark вызывает ошибку времени компиляции

метод bark не определен как метод для назначенного типа Animal, что приведет к проблеме времени компиляции; это может быть решено путем приведения;

((Dog)a).bark();
11 голосов
/ 22 мая 2009

Ключ находится в следующей строке:

Animal a = new Dog();

Хотя был создан новый экземпляр Dog, его ссылка на a объявлена ​​как тип Animal. Поэтому любые ссылки на a делают new Dog обработанным как Animal.

Следовательно, если Animal не имеет метода bark, следующая строка вызовет ошибку компилятора:

a.bark();

Несмотря на то, что a проверяется на наличие экземпляра Dog, а a instanceof Dog действительно возвращает true, переменная a все еще имеет тип Animal, поэтому блок внутри оператора if по-прежнему обрабатывает a как Animal.

Это функция статически типизированных языков , где переменным присваивается тип заранее и проверяется во время компиляции, чтобы убедиться, что типы совпадают. Если этот код был выполнен на динамически типизированном языке , где типы проверяются во время выполнения, то может быть разрешено что-то вроде следующего:

var a = new Dog();
if (a instanceof Dog)
    a.bark();

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

5 голосов
/ 22 мая 2009

In Head First Java они используют очень хорошую аналогию пульта ДУ телевизора для эталона и вашего телевизора в качестве объекта , на который указывает эталон , Если на вашем пульте есть только кнопки (методы) для включения, выключения, переключения каналов вверх и вниз, а также увеличения и уменьшения громкости, не имеет значения, какие классные функции есть у вашего телевизора. Вы все еще можете делать только те несколько основных вещей с вашего пульта. Например, вы не можете отключить звук своего телевизора, если на пульте дистанционного управления нет кнопки отключения звука.

Справочник Animal знает только о методах Animal. Неважно, какие другие методы есть у базового объекта, вы не можете получить к ним доступ из ссылки на Animal.

4 голосов
/ 13 мая 2011

Если идея состоит в том, чтобы напечатать метод подкласса из объекта суперкласса, это будет работать:

Вместо Animal a = new Dog(); if (a instanceof Dog){ a.bark(); } изменить на

Animal a = new Dog();

if (a instanceof Dog){ 
    Dog d = (Dog) a; 
    d.bark();
}  

Это возвращает суперкласс обратно в подкласс и печатает его. хотя это плохой дизайн, это один из способов узнать, на какой объект дочернего класса он динамически указывает.

4 голосов
/ 22 мая 2009

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

Animal a = new Cat();

и у линии коры нет возможности узнать, что у вас нет.

2 голосов
/ 04 мая 2014

В Java (только язык, который я знаю) вы можете создать пустой метод и вызвать его в суперклассе. Затем вы можете переопределить его в подклассе, чтобы делать все, что вы хотите. Таким образом, суперкласс вызывает метод своего подкласса.

public class Test {
    public static void main(String[] args){
        Snake S = new Snake();
        Dog d = new Dog();
    }
}


class Animal{ //Super Class
    public Animal(){
        bark(); //calls bark when a new animal is created
    }
    public void bark(){
        System.out.println("this animal can't bark");
    }
}



class Dog extends Animal{ //Subclass 1
    @Override
    public void bark(){
        System.out.println("Woof");
    }
}



class Snake extends Animal{//Subclass 2
    public void tss(){
    }
}

Этот код вызывает объект Snake, затем вызывает объект Dog. Это записывает это в консоль:

this animal can't bark
Woof

У Снейка нет метода коры, поэтому вызывается метод суперкласса. Он пишет первую строку в консоль. У Dog есть метод лая, поэтому суперкласс вызывает его. Записывает вторую строку в консоль.

2 голосов
/ 22 мая 2009

К вашему сведению, это не очень хороший дизайн.

Примерно в любое время у вас есть код этой формы:

if (x instanceof SomeClass)
{
   x.SomeMethod();
}

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

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

1 голос
/ 22 мая 2009

"Я сказал 2, когда мы проверяем, является ли объект собакой; поскольку собака - это класс с методом лая, если это так, мы вызываем его, который выведет: s"

Ваше обоснование правильное, но это не так.

Java - это статический типизированный язык, который означает, что валидность методов, на которые может ответить объект, проверяется в время компиляции .

Вы можете подумать, чек:

if( a instanceof Dog ) 

Подойдет, но на самом деле это не так. Компилятор проверяет «интерфейс» объявленного типа (в данном случае Animal). «Интерфейс» состоит из методов, объявленных в классе Animal.

Если метод bark () не определен в суперклассе Animal , компилятор говорит: «Эй, это не сработает».

Это полезно, потому что «иногда» мы делаем опечатки во время кодирования (например, вместо этого вводим barck ())

Если компилятор не предупреждает нас об этом, вам нужно будет найти его во время выполнения, а не всегда с четким сообщением (например, JavaScript в IE говорит что-то вроде «неожиданного объекта»)

Тем не менее, статический типизированный язык, такой как java, позволяет нам форсировать вызов. В этом случае используется оператор «cast» ()

Как это

1. Animal a = new Dog();
2.  if (a instanceof Dog){
3.     Dog imADog = ( Dog ) a;
4.     imADog.bark();
5. }

В строке 3 вы «приводите» к типу Dog, поэтому компилятор может проверить, является ли лай действительным сообщением.

Это инструкция для компилятора, говорящая: «Эй, я программист, я знаю, что делаю». И компилятор, проверки, ОК, собака, может получить сообщение bark (), продолжить. Тем не менее, если во время выполнения животное не является собакой, возникнет исключение во время выполнения.

Состав также может быть сокращен как:

if( a instanceof Dog ) {
   ((Dog)a).bark();  
}

Это будет работать.

Итак, правильный ответ: 4:" вызов лая вызывает ошибку времени компиляции "

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

...