Не получается вызвать метод так как в суперклассе этого класса переменная реализации интерфейса = null
Изучаю паттерны и у меня есть родительский класс Duck
и его подклассы, есть метод performShowLevelOfAggression()
и переменная реализации интерфейса Aggressable
она определяется в подклассе и потом в performShowLevelOfAggression()
выполняется её метод. Я вызываю метод performShowLevelOfAggression()
подкласса MalardDuck
, но метод не вызывается и пишет ошибку:
Cannot invoke "org.example.Aggressable.LevelOfAggression()" because "this.aggressable" is null at org.example.Duck.performShowLevelOfAggression(Duck.java:15) at org.example.Main.main(Main.java:14)
хотя делаю всё по книге
Код:
public abstract class Duck {
protected Aggressable aggressable;
public Duck(){}
protected void performShowLevelOfAggression()
{
aggressable.LevelOfAggression();
}
public class MalardDuck extends Duck {
public Aggressable aggressable = new ActiveAggression();
public MalardDuck(){}
}
public interface Aggressable {
public void LevelOfAggression();
}
public class ActiveAggression implements Aggressable{
@Override
public void LevelOfAggression() {
System.out.println("ActiveAggression");
}
}
public class Main {
public static void main(String[] args) {
MalardDuck malard = new MalardDuck();
malard.performShowLevelOfAggression();
}
}
Ответы (1 шт):
Вряд ли в книге написано, что нужно создавать ещё одно публичное поле в подклассе и скрывать таким образом защищённое неинициализированное поле родительского класса:
public abstract class Duck {
protected Aggressable aggressable; // по умолчанию это поле null
// ...
}
public class MalardDuck extends Duck {
public Aggressable aggressable = new ActiveAggression(); // !!! НЕВЕРНО
// ...
}
Дело в том, что поскольку метод performShowLevelOfAggression
реализован только в родительском классе, он будет обращаться к полю этого родительского класса, которое равно null
.
Достаточно было бы проинициализировать защищённое поле родительского класса в конструкторе подкласса, так как для этого собственно поле и объявлено как protected
public class MalardDuck extends Duck {
public MalardDuck() {
this.aggressable = new ActiveAggression(); // вот и всё!
}
}
Разумеется, данное затруднение можно было бы разрешить, переопределив метод performShowLevelOfAggression
в классе-потомке, тогда этот метод обращался бы к своему перекрывающему полю agressable
, но в целом такой дизайн плохой, так как он приводит к ненужному дублированию кода.
public class MalardDuckBad extends Duck {
// скрывает поле родительского класса
public Aggressable aggressable = new ActiveAggression();
public MalardDuck() {}
// дублируется код из родительского класса
@Override
public void performShowLevelOfAggression() {
aggressable.LevelOfAggression();
}
}
Также следует отметить, что метод performShowLevelOfAggression
не совсем корректно объявлен как protected
, так как его нельзя будет вызвать для экземпляра Duck
из другого пакета, по идее он должен был бы быть публичным.