Лекция 06. Наследование и полиморфизм ####################################################################################### Наследование =========================================================== Понятие наследования ----------------------------------------------------------- **Наследование** - механизм передачи данных и кода от одних классов другим, тип отношений между классами. Для выражения наследования в языке имеется специальное ключевое слово **extends** .. code-block:: java class A {...} class B extends A {...} Рассмотрим следующие классы: .. code-block:: java class Person { protected String name; protected int born; public Person(String name,int born) { this.name=name; this.born=born; } public void info() { System.out.println(name+"-"+born); } } Класс **Employee** (работник) мы получаем с использованием наследования от класса **Person** .. code-block:: java class Employee extends Person { protected int payment; public Employee(String name,int born,int pay) { super(name,born); this.payment=pay; } } **super** означает ссылку на родительский класс, которому мы передаем данные для конструктора. Ключевое слово **super** `````````````````````````````````````````````````````````` Вызов конструктора непосредственного суперкласса: **super (parameters)** – вызов должен быть первым в конструкторе подкласса Доступ к элементу суперкласса, скрытому элементом подкласса: **super.var_name** или **super.metod_name()** (super не влияет на тип доступа) Понятие наследования `````````````````````````````````````````````````````````` Теперь можно создать работника и выяснить личную информацию: .. code-block:: java public class Program { public static void main(String[] args) { Employee emp=new Employee("Иванов А.А.",1988,35000); emp.info(); } } Вывод: .. code-block:: none Иванов А.А. - 1988 Запрет наследования `````````````````````````````````````````````````````````` В некоторых случаях наследование можно запретить: .. code-block:: java final class Person { // наследовать нельзя! } С помощью **final** можно запретить переопределение избранных методов. Модификатор **final** `````````````````````````````````````````````````````````` .. code-block:: java //неизменяемая переменная final double pi = 3.14; .. code-block:: java //метод, для которого запрещено переопределение (overriding) final int getX() {…} .. code-block:: java //класс, для которого запрещено наследование final class A {…} Переопределение методов ----------------------------------------------------------- В производных классах можно переопределять методы родительских классов: .. code-block:: java class Person { public void info() {...} } class Employee extends Person { public void info() {...} } Для переопределения нужно точно воспроизвести заголовок родительского метода. Для надежности в производном классе используют аннотацию: .. code-block:: java @Override public void info() {...} **Переопределение метода (overriding)** – создание в подклассе метода, совпадающего по сигнатуре с методом суперкласса. Переопределение не стоит путать с **перегрузкой**, когда в класс добавляется метод с тем же именем, но с другим набором параметров. Полиморфизм =========================================================== Динамическая диспетчеризация ----------------------------------------------------------- #. Динамическая диспетчеризация ссылок #. Динамическая диспетчеризация методов Ссылочная переменная суперкласса (родителя) может ссылаться на объект подкласса (на том основании, что экземпляры производного класса являются разновидностями базового). .. code-block:: java class Point { public int x, int y; } class Point3D extends Point { public int z; } Point Pobj = new Point(); Point3D Cobj = new Point3D(); Pobj = Cobj; Pobj.x = 1; //верно! x определена в Point Pobj.z = 10; //ошибка! z не определена в Point **Динамическая диспетчеризация методов** – это механизм, позволяющий определить какой из переопределенных методов нужно вызвать, во время выполнения, а не во время компиляции. .. code-block:: java class A { public int get() {...}} class B extends A {public int get() {...}} class C extends B {public int get() {...}} ... A one=new A(); A two=new B(); A three=new C(); ... one.get() two.get() three.get() Абстрактные методы и классы ----------------------------------------------------------- Абстрактные классы и методы `````````````````````````````````````````````````````````` В Java существуют **абстрактные** классы, которые можно использовать только для наследования, но не для создания экземпляров (объектов). Производный класс должен переопределить и реализовать все абстрактные методы из базового класса, иначе он сам будет считаться абстрактным. Модификатор **abstract** `````````````````````````````````````````````````````````` Можно объявлять абстрактыми методы и классы. Если в классе есть хотя бы один абстрактный метод, то класс должен быть объявлен абстрактным. Любой подкласс абстрактного класса должен или реализовать все его абстрактные методы или сам должен быть объявлен абстрактным Абстрактные классы и методы `````````````````````````````````````````````````````````` Объявление абстрактного метода: .. code-block:: java abstract (); Абстрактный класс: .. code-block:: java abstract class { ... } Интерфейсы ----------------------------------------------------------- **Интерфейсы** - это особый вид классов. #. Интерфейсы допускают множественное наследование #. Все методы – абстрактные (без модификатора abstract) #. Все переменные – **static** и **final** (без соотв. модификаторов), необходима инициализация #. Все переменные и методы – **public** (без модификатора) Особенности реализации интерфейсов: #. Методы, которые реализуют интерфейс, должны быть объявлены как public. Сигнатура типа реализующего метода должна точно соответствовать сигнатуре типа, указанной в определении интерфейса. #. Если класс включает интерфейс, но реализует не все его методы, то такой класс должен быть объявлен как абстрактный. #. Если класс реализует интерфейс, унаследованный от другого интерфейса, класс должен реализовать все методы, определенные в цепочке наследования интерфейсов. .. code-block:: java interface Callback { void callback(int param); } class Client implements Callback { public void callback(int p) { System.out.println("callback called with " + p); } } .. code-block:: java interface Callback { void callback(int param); } class Client implements Callback { public void callback(int p) //реализация метода интерфейса { System.out.println("callback called with " + p); } int getSquare(int p) //собственный метод класса { System.out.println("square = " + (p*p)); } } .. code-block:: java class TestIface { public static void main(String args[ ]) { Callback с = new Client(); c.callback(42); //верно! c.getSquare(42); //ошибка! ссылочная переменная //интерфейсного типа не может обращаться к //собственным методам классов } } Интерфейсы можно использовать для импорта в различные классы совместно используемых констант. В том случае, когда вы реализуете в классе какой-либо интерфейс, все имена переменных этого интерфейса будут видимы в классе как константы. Если интерфейс не включает в себя методы, то любой класс, объявляемый реализацией этого интерфейса, может вообще ничего не реализовывать. .. code-block:: java interface Constants { int NO = 0; int YES = 1; int MAYBE = 2; int LATER = 3; int SOON = 4; int NEVER = 5; } .. code-block:: java class Question implements Constants { Random rand = new Random(); int ask() { int prob=(int)(100 * rand.nextDouble()); if (prob<30) return NO; else if (prob<60) return YES; else if (prob<75) return LATER; else if (prob<98) return SOON; else return NEVER; } } .. image:: _static/06/classes-ifaces.png Класс Object =========================================================== Все классы, создаваемые разработчиком, имеют наследование от системного класса **Object** Для своих классов можно переопределить: * **toString()** - преобразование в строку * **hashCode()** - вычисление хэш-кода объекта * **equals()** - проверка двух объектов на равенство * **clone()** - создание копии объекта * **finalize()** - аналог деструктора Нельзя переопределить: * **getClass()** - получение типа объекта Класс **Object** `````````````````````````````````````````````````````````` * **Object clone()** – создает новый объект, являющийся копией вызывающего * **boolean equals(Object object)** – определяет, является ли один объект равным другому * **void finalize()** – завершающие действия перед вызовом gc * **String toString()** – возвращает строку, содержащую описание вызывающего объекта. Этот метод вызывается автоматически, когда объект выводится методом print() или println(). Многие классы переопределяют данный метод, приспосабливая описание специально для типов объектов, которые они создают. При определении **equals()** обычно требуется переопределить **hashCode()** Класс Object `````````````````````````````````````````````````````````` Для **hashCode()** должны выполняться правила: * Во время работы значение хэш-кода не меняется, если объект не был изменен. * Все одинаковые по содержанию объекты одного типа должны иметь одинаковые коды. * Различные по содержанию объекты одного типа должны иметь разные хэш-коды. Вопросы для самоконтроля =============================================