Сложность с концепцией наследования Java и переопределения
Ссылочная переменная суперкласса может содержать объект подкласса, но с помощью этой переменной вы можете получить доступ только к членам суперкласса, поэтому для доступа к членам обоих классов рекомендуется всегда создавать ссылочную переменную для подкласса.
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dogs can walk and run");
}
public void bark() {
System.out.println("Dogs can bark");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal reference and object
Animal b = new Dog(); // Animal reference but Dog object
a.move(); // runs the method in Animal class
b.move(); // runs the method in Dog class
b.bark();
}
}
выход:
TestDog.java:26: error: cannot find symbol
b.bark();
^
symbol: method bark()
location: variable b of type Animal
1 error
Здесь я не понимаю, почему объект 'b' может обращаться к Dog.move(), а не к Dog.bark(), потому что в приведенном выше утверждении говорится, что он может получить доступ только к членам суперкласса, а не к подклассу. Следуя этой логике, вывод b.move() должен быть "Животные могут двигаться", а не "Собаки могут ходить и бегать". Но это не так. Может ли кто-нибудь помочь мне с этим? Заранее спасибо!
4 ответа
Поздравляю - вы только что обнаружили полиморфизм.
В Java классы связаны динамически. То есть, если вы вызываете метод, вызывается реализация объекта (в вашем случае Dog
), а не метод ссылочного типа (в вашем случае Animal
).
Это позволяет перезаписывать методы и заменять или выполнять их реализацию.
С другой стороны, вы можете получить доступ только к тем методам, которые доступны в типе, на который вы ссылаетесь, а не в типе реализации (в вашем случае Animal
). Чтобы вызвать методы экземпляра, вы должны будете использовать его как ссылочный тип (в вашем случае Dog
).
Это не скомпилируется, так как Animal
не имеет метода с именем bark
,
Подумайте об этом так, все собаки - животные, но не все животные - собаки. Все собаки лают, но не все животные лают.
В вашем вопросе Animal
это родительский класс, который не имеет bark()
метод, так что метод не переопределяется. Если бы вы могли получить доступ bark()
из родительского класса, не объявляя ни абстрактный метод, ни определяющий его, тогда это будет нарушением принципа полиморфизма.
Если вы действительно хотите получить к нему доступ таким образом, то вы можете определить abstract public void bark();
в вашем родителе или доступ к этому методу, приведя тип, как это
((Dog) b).bark();
Этот код неверен, так как строка b.bark();
выдаст ошибку компилятора, потому что b определяется только как Animal, который не может использовать bark().
Если вы измените Animal b = new Dog();
в Dog d = new Dog();
это будет работать правильно.
Вы перепутали наследство. Собака может делать то же, что и животное, а не наоборот.
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
@Override public void move() {
System.out.println("Dogs can walk and run");
}
public void bark() {
System.out.println("Dogs can bark");
}
public void moveSuper() {
super.move();
}
}
public class TestDog {
public static void main(final String args[]) {
final Animal a = new Animal(); // Animal reference and object
a.move(); // runs the method in Animal class
final Dog d = new Dog(); // Animal reference but Dog object
d.move(); // runs the method in Dog class
d.bark();
d.moveSuper();
}
}