Методы
Рассмотрим случай переопределения (overriding) методов:
class Parent { public int getValue() { return 0; } } class Child extends Parent { public int getValue() { return 1; } }
И строки, демонстрирующие работу с этими методами:
Child c = new Child(); System.out.println(c.getValue()); Parent p = c; System.out.println(p.getValue());
Результатом будет:
1 1
Можно видеть, что родительский метод полностью перекрыт, значение 0 никак нельзя получить через ссылку, указывающую на объект класса Child. В этом ключевая особенность полиморфизма – наследники могут изменять родительское поведение, даже если обращение к ним производится по ссылке родительского типа. Напомним, что, хотя старый метод снаружи уже недоступен, внутри класса-наследника к нему все же можно обратиться с помощью super.
Рассмотрим более сложный пример:
class Parent { public int getValue() { return 0; } public void print() { System.out.println(getValue()); } }
class Child extends Parent { public int getValue() { return 1; } }
Что появится на консоли после выполнения следующих строк?
Parent p = new Child(); p.print();
С помощью ссылки типа Parent вызывается метод print(), объявленный в классе Parent. Из этого метода делается обращение к getValue(), которое в классе Parent возвращает 0. Но компилятор уже не может предсказать, к динамическому методу какого класса произойдет обращение во время работы программы. Это определяет виртуальная машина на основе объекта, на который указывает ссылка. И раз этот объект порожден от Child, то существует лишь один метод getValue().
Результатом работы примера будет:
1
Данный пример демонстрирует, что переопределение методов должно производиться с осторожностью. Если слишком сильно изменить логику их работы, нарушить принятые соглашения (например, начать возвращать null в качестве значения ссылочного типа, если родительский метод такого не допускал), это может привести к сбоям в работе родительского класса, а значит, объекта наследника. Более того, существуют и некоторые обязательные ограничения.
Вспомним, что заголовок метода состоит из модификаторов, возвращаемого значения, сигнатуры и throws-выражения. Сигнатура (имя и набор аргументов) остается неизменной, если говорить о переопределении. Возвращаемое значение также не может меняться, иначе это приведет к появлению двух разных методов с одинаковыми сигнатурами.
Рассмотрим модификаторы доступа.
Рассмотрим случай переопределения (overriding) методов:
class Parent { public int getValue() { return 0; } } class Child extends Parent { public int getValue() { return 1; } }
И строки, демонстрирующие работу с этими методами:
Child c = new Child(); System.out.println(c.getValue()); Parent p = c; System.out.println(p.getValue());
Результатом будет:
1 1
Можно видеть, что родительский метод полностью перекрыт, значение 0 никак нельзя получить через ссылку, указывающую на объект класса Child. В этом ключевая особенность полиморфизма – наследники могут изменять родительское поведение, даже если обращение к ним производится по ссылке родительского типа. Напомним, что, хотя старый метод снаружи уже недоступен, внутри класса-наследника к нему все же можно обратиться с помощью super.
Рассмотрим более сложный пример:
class Parent { public int getValue() { return 0; } public void print() { System.out.println(getValue()); } }
class Child extends Parent { public int getValue() { return 1; } }
Что появится на консоли после выполнения следующих строк?
Parent p = new Child(); p.print();
С помощью ссылки типа Parent вызывается метод print(), объявленный в классе Parent. Из этого метода делается обращение к getValue(), которое в классе Parent возвращает 0. Но компилятор уже не может предсказать, к динамическому методу какого класса произойдет обращение во время работы программы. Это определяет виртуальная машина на основе объекта, на который указывает ссылка. И раз этот объект порожден от Child, то существует лишь один метод getValue().
Результатом работы примера будет:
1
Данный пример демонстрирует, что переопределение методов должно производиться с осторожностью. Если слишком сильно изменить логику их работы, нарушить принятые соглашения (например, начать возвращать null в качестве значения ссылочного типа, если родительский метод такого не допускал), это может привести к сбоям в работе родительского класса, а значит, объекта наследника. Более того, существуют и некоторые обязательные ограничения.
Вспомним, что заголовок метода состоит из модификаторов, возвращаемого значения, сигнатуры и throws-выражения. Сигнатура (имя и набор аргументов) остается неизменной, если говорить о переопределении. Возвращаемое значение также не может меняться, иначе это приведет к появлению двух разных методов с одинаковыми сигнатурами.
Рассмотрим модификаторы доступа.