Что не так с этим примером наследования свойств Java?
Inheritance.java
public class InheritanceExample {
static public void main(String[] args){
Cat c = new Cat();
System.out.println(c.speak());
Dog d = new Dog();
System.out.println(d.speak());
}
}
Animal.java
public class Animal {
protected String sound;
public String speak(){
return sound;
}
}
Cat.java
public class Cat extends Animal {
protected String sound = "meow";
}
Dog.java
public class Dog extends Animal {
protected String sound = "woof";
}
Вывод:
null
null
Мои животные не могут говорить. Так грустно.
Ответы
Ответ 1
Поля не являются полиморфными. Вы объявили три совершенно разных поля... те, что были в тэгах Cat
и Dog
, или спрятали в Animal
.
Самый простой (но не обязательно лучший) способ получить ваш текущий код - удалить sound
из Cat
и Dog
и установить значение унаследованного поля sound
в конструкторе для Cat
и Dog
.
Лучшим подходом было бы сделать абстракцию Animal
и дать ему защищенный конструктор, который принимает звук... конструкторы Cat
и Dog
затем вызывают super("meow")
и super("woof")
соответственно:
public abstract class Animal {
private final String sound;
protected Animal(String sound) {
this.sound = sound;
}
public String speak(){
return sound;
}
}
public class Cat extends Animal {
public Cat() {
super("meow");
}
}
public class Dog extends Animal {
public Dog() {
super("woof");
}
}
Ответ 2
Вы не можете переопределять поля классов, а только методы. Поле sound
в классах Dog
и Cat
фактически скрывает поле sound
в суперклассе Animal
.
Однако вы можете получить доступ к полям суперкласса из подклассов, чтобы вы могли сделать что-то вроде этого:
public class Dog extends Animal {
public Dog() {
sound = "woof";
}
}
public class Cat extends Animal {
public Cat() {
sound = "meow";
}
}
Или вы можете создать абстрактный тег Animal
и объявить также тег метода speak
, а затем определить его в подклассах:
public abstract class Animal {
public abstract String speak();
}
public class Dog extends Animal {
public String speak {
return "woof";
}
}
public class Cat extends Animal {
public String speak {
return "meow";
}
}
Ответ 3
Вы скрываете поля. sound
в Animal
не совпадает с String
как sound
в Cat
.
Одно из возможных решений - создать конструктор и просто сказать
super.sound = "meow";
а не в классе класса, говорящем
protected String sound = "meow";
чтобы установить поле.
Ответ 4
Метод будет искать в пространстве имен своего собственного класса для разрешения полей. Хотя методы, определенные в подклассах, могут искать иерархию для разрешения полей, то же самое не верно для классов, определенных выше в иерархии, т.е. Суперклассы не будут смотреть вниз по иерархии для разрешения полей [и их значений]
Ответ 5
Вы shadowing поле, унаследованное от Animal
. У вас есть несколько вариантов, но самый красивый способ сделать это - передать звук в конструкторе:
public class Animal {
private final String sound;
protected Animal(String sound){
if (sound == null)
throw new NullPointerException("sound");
this.sound = sound;
}
public String speak(){
return sound;
}
}
public class Cat extends Animal {
public Cat(){ super("meow"); }
}
public class Dog extends Animal {
public Dog(){ super("woof"); }
}
Таким образом, вы можете убедиться, что Animal
всегда имеет правильный звук, прямо из конструкции.
Ответ 6
Способ Java (TM) - объявить защищенную строку getSound() в Animal.java и реализовать ее в подклассах.
Ответ 7
Ты не позволил своим животным говорить!
вы должны сделать следующее:
Cat.java:
public class Cat extends Animal {
// protected String sound = "meow";
public Cat(){
this.sound = "cat";
}
}
Dog.java:
public class Dog extends Animal {
// protected String sound = "woof";
public Dog(){
this.sound = "dog";
}
}
только потому, что в Cat или Dog есть два члена "звук", а тот, унаследованный от Animal
скрывается без значения (поэтому он печатает нуль), другой - для Cat или Dog, которому присваивается значение.
Поэтому вы должны использовать указатель 'this', чтобы процитировать исходный "звук" участника.