Лекция 06. Наследование и полиморфизм¶
Наследование¶
Понятие наследования¶
Наследование - механизм передачи данных и кода от одних классов другим, тип отношений между классами.
Для выражения наследования в языке имеется специальное ключевое слово extends
class A {...}
class B extends A {...}
Рассмотрим следующие классы:
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
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 не влияет на тип доступа)
Понятие наследования¶
Теперь можно создать работника и выяснить личную информацию:
public class Program {
public static void main(String[] args) {
Employee emp=new Employee("Иванов А.А.",1988,35000);
emp.info();
}
}
Вывод:
Иванов А.А. - 1988
Запрет наследования¶
В некоторых случаях наследование можно запретить:
final class Person { // наследовать нельзя!
}
С помощью final можно запретить переопределение избранных методов.
Модификатор final¶
//неизменяемая переменная
final double pi = 3.14;
//метод, для которого запрещено переопределение (overriding)
final int getX() {…}
//класс, для которого запрещено наследование
final class A {…}
Переопределение методов¶
В производных классах можно переопределять методы родительских классов:
class Person {
public void info() {...}
}
class Employee extends Person {
public void info() {...}
}
Для переопределения нужно точно воспроизвести заголовок родительского метода.
Для надежности в производном классе используют аннотацию:
@Override
public void info() {...}
Переопределение метода (overriding) – создание в подклассе
метода, совпадающего по сигнатуре с методом суперкласса.
Переопределение не стоит путать с перегрузкой, когда в класс добавляется метод с тем же именем, но с другим набором параметров.
Полиморфизм¶
Динамическая диспетчеризация¶
- Динамическая диспетчеризация ссылок
- Динамическая диспетчеризация методов
Ссылочная переменная суперкласса (родителя) может ссылаться на объект подкласса (на том основании, что экземпляры производного класса являются разновидностями базового).
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
Динамическая диспетчеризация методов – это механизм, позволяющий определить какой из переопределенных методов нужно вызвать, во время выполнения, а не во время компиляции.
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¶
Можно объявлять абстрактыми методы и классы.
Если в классе есть хотя бы один абстрактный метод, то класс должен быть объявлен абстрактным.
Любой подкласс абстрактного класса должен или реализовать все его абстрактные методы или сам должен быть объявлен абстрактным
Абстрактные классы и методы¶
Объявление абстрактного метода:
abstract <type> <name> (<params>);
Абстрактный класс:
abstract class <name> {
...
}
Интерфейсы¶
Интерфейсы - это особый вид классов.
- Интерфейсы допускают множественное наследование
- Все методы – абстрактные (без модификатора abstract)
- Все переменные – static и final (без соотв. модификаторов), необходима инициализация
- Все переменные и методы – public (без модификатора)
Особенности реализации интерфейсов:
- Методы, которые реализуют интерфейс, должны быть объявлены как public. Сигнатура типа реализующего метода должна точно соответствовать сигнатуре типа, указанной в определении интерфейса.
- Если класс включает интерфейс, но реализует не все его методы, то такой класс должен быть объявлен как абстрактный.
- Если класс реализует интерфейс, унаследованный от другого интерфейса, класс должен реализовать все методы, определенные в цепочке наследования интерфейсов.
interface Callback
{
void callback(int param);
}
class Client implements Callback
{
public void callback(int p) {
System.out.println("callback called with " + p);
}
}
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));
}
}
class TestIface
{
public static void main(String args[ ])
{
Callback с = new Client();
c.callback(42); //верно!
c.getSquare(42); //ошибка! ссылочная переменная
//интерфейсного типа не может обращаться к
//собственным методам классов
}
}
Интерфейсы можно использовать для импорта в различные классы совместно
используемых констант. В том случае, когда вы реализуете в классе
какой-либо интерфейс, все имена переменных этого интерфейса будут
видимы в классе как константы. Если интерфейс не включает в себя методы,
то любой класс, объявляемый реализацией этого интерфейса, может вообще
ничего не реализовывать.
interface Constants
{
int NO = 0;
int YES = 1;
int MAYBE = 2;
int LATER = 3;
int SOON = 4;
int NEVER = 5;
}
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;
}
}

Класс Object¶
Все классы, создаваемые разработчиком, имеют наследование от системного класса Object
Для своих классов можно переопределить:
- toString() - преобразование в строку
- hashCode() - вычисление хэш-кода объекта
- equals() - проверка двух объектов на равенство
- clone() - создание копии объекта
- finalize() - аналог деструктора
Нельзя переопределить:
- getClass() - получение типа объекта
- Object clone() – создает новый объект, являющийся копией вызывающего
- boolean equals(Object object) – определяет, является ли один объект равным другому
- void finalize() – завершающие действия перед вызовом gc
- String toString() – возвращает строку, содержащую описание вызывающего объекта. Этот метод вызывается автоматически, когда объект выводится методом print() или println(). Многие классы переопределяют данный метод, приспосабливая описание специально для типов объектов, которые они создают.
При определении equals() обычно требуется переопределить hashCode()
Для hashCode() должны выполняться правила:
- Во время работы значение хэш-кода не меняется, если объект не был изменен.
- Все одинаковые по содержанию объекты одного типа должны иметь одинаковые коды.
- Различные по содержанию объекты одного типа должны иметь разные хэш-коды.