На этом шаге мы рассмотрим абстрактные классы
Чем дальше вверх по иерархии наследования, тем более универсальными и абстрактными становятся классы. В некотором смысле родительские классы, находящиеся на верхней ступени иерархии, становятся настолько абстрактными, что их рассматривают как основу для разработки других классов, а не как классы, позволяющие создавать конкретные объекты. Например, сотрудник - это человек, а человек может быть и студентом. Поэтому расширим иерархию, в которую входит класс Employee, добавив в нее классы Person и Student. Отношения наследования между всеми этими классами показаны на рисунке ниже.
Рис. 1. Иерархия наследования для класса Person и его подклассов
Возникает разумный вопрос: а зачем вообще столь высокий уровень абстракции? Ответ на него простой: существуют определенные свойства, характерные для каждого человека, например, имя. Ведь у сотрудников вообще и у студентов в частности имеются свои имена, и поэтому при внедрении общего суперкласса придется перенести метод getName() на более высокий уровень в иерархии наследования.
Введем новый метод getDescription(), для вывода на экран краткой характеристики человека. Например, этот метод может вывести на экран следующее:
an employee with a salary of $50,000.00 a student, ajoring in computer science
Для классов Employee и Student такой метод реализуется довольно просто. Но какие сведения о человеке следует разместить в класс Person? Ведь в нем ничего нет, кроме имени. Разумеется, можно было бы реализовать метод Person.getDescription(), возвращающий пустую строку. Но есть способ намного лучше. От реализации этого метода в классе Person можно вообще отказаться, если воспользоваться ключевым словом abstract, как показано ниже.
/*Реализация не требуется*/ public abstract String getDescription();
Класс, содержащий один или несколько абстрактных методов, нужно объявить абстрактным следующим образом:
abstract class Person { ... public abstract String getDescription(); }
Помимо абстрактных методов, абстрактные классы могут содержать конкретные поля и методы. Например, в классе Person хранится имя человека и содержится конкретный метод, возвращающий это имя, как показано ниже:
abstract class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return this.name; } public abstract String getDescription(); }
Некоторые программисты не осознают, что абстрактные классы могут содержать конкретные методы. Общие поля и методы (будь то абстрактные или конкретные) следует всегда перемещать в суперкласс, каким бы он ни был: абстрактным или конкретным.
Абстрактные методы представляют собой прототипы методов, реализованных в подклассах. Расширяя абстрактный класс, можно оставить некоторые или все абстрактные методы неопределенными. При этом подкласс также станет абстрактным. Если даже определить все методы, то и тогда подкласс не перестанет быть абстрактным.
Определим класс Student, расширяющий абстрактный класс Person и реализующий метод getDescription(). Ни один из методов в классе Student не является абстрактным, поэтому нет никакой необходимости объявлять сам класс абстрактным. Но заметим, что класс может быть объявлен абстрактным, даже если он и не содержит ни одного абстрактного метода.
Создать экземпляры абстрактного класса нельзя. Например, приведенное ниже выражение ошибочно. Но можно создать объекты конкретного подкласса:
new Person("Name");
Следует иметь ввиду, что для абстрактных классов можно создавать объектные переменные, но такие переменные должны ссылаться на объект не абстрактного класса. Рассмотрим следующую строку кода:
Person person = new Student("Name", "major");
Здесь person - переменная абстрактного типа Person, ссылающаяся на экземпляр не абстрактного подкласса Student.
Определим конкретный подкласс Student, расширяющий абстрактный класс Person, следующим образом:
class Student extends Person { private String major; public Student(String name, String major) { super(name); this.major = major; } public String getDescription() { return "a student majoring in " + major; } }
В этом подклассе определяется метод getDescription(). Все методы из класса Student являются конкретными, а следовательно, класс больше не является абстрактным.
На следующем шаге мы рассмотрим пример использования абстракных классов