В иерархии классов часто присутствуют методы с одинаковой сигнатурой и одинаковым возвращаемым значением как в суперклассе, так и в подклассе. В этом случае говорят, что метод суперкласса переопределяется в подклассе. Если переопределяемый метод вызывается из подкласса, то всегда выбирается тот вариант метода, который определен в подклассе. А вариант метода, определенный в суперклассе, скрывается. Рассмотрим в качестве примера следующий фрагмент кода: // Переопределение метода, class А { int i, j; A(int a, int b) { i = a; j = b; } // отобразить переменные i и j void show { System.out.println("i and j: " + i + " " + j) ; } } class В extends A { int k; В(int a, int b, int c) { super(a, b); k = c; } // Отображение переменной к. Данный метод переопределяет // метод show из класса А. void show { System.out.println("k: " + к); } } class Override { public static void main(String args[]) { В subOb = new B(l, 2, 3) ; subOb.show; // вызвать метод show из класса В } }
Выполнение этого фрагмента кода дает следующий результат: к: 3
Когда метод show вызывается для объекта типа В, выбирается вариант этого метода, определенный в классе В. Таким образом, вариант метода show в классе В переопределяет вариант одноименного метода, объявленный в классе А.
Если требуется обратиться к исходному варианту переопределяемого метода, т.е. тому, который определен в суперклассе, следует воспользоваться ключевым словом super. Например, в приведенном ниже варианте класса В из метода show вызывается вариант того же метода, определенный в суперклассе. При этом отображаются все переменные экземпляра. class В extends А { int к; В(int a, int b, int с) { super (а, Ь); к = с; } void show { // Использование ключевого слова super для // вызова метода show, определенного в классе А. super.show; System.out.println("k: " + k); } }
Если подставить новый вариант метода show в предыдущую версию рассматриваемого здесь фрагмента кода, результат его выполнения изменится и будет иметь следующий вид: i and j: 1 2 k: 3
В данном случае super. show — это вызов метода show , определенного в суперклассе.
Переопределение метода происходит только в том случае, если сигнатуры переопределяемого и переопределяющего методов совпадают. В противном случае происходит обычная перегрузка методов. Рассмотрим следующую видоизмененную версию предыдущего примера: /* Методы с разными сигнатурами не переопределяются, а перегружаются. */ class А { int i, j; A(int a, int b) { i = a; j = b; } // отобразить переменные i и j void show { System.out.println("i and j: " + i + " " + j) ; } } // создать подкласс путем расширения класса Л class В extends А { int к; В (int a, int b, int с) { super(а, Ь); к = с; } // Сигнатуры данного метода и метода show из класса А отличаются, // поэтому вместо переопределения происходит перегрузка метода. void show(String msg) { System.out.println(msg + k); } } class Overload { public static void main(String args[]) { В subOb = new В(1, 2, 3); subOb.show("This is k: "); // вызывается метод show из класса В subOb.show; // вызывается метод show из класса А } }
Выполнение этого фрагмента кода дает следующий результат: This is k: 3 i and j: 1 2
На этот раз в варианте метода show из класса В предусмотрен строковый параметр. И благодаря этому сигнатура данного метода отличается от сигнатуры метода show из класса А, для которого параметры не предусмотрены. В результате переопределение метода не происходит. Поддержка полиморфизма
в переопределяемых методах Примеры из предыдущего раздела демонстрируют переопределение методов, но по ним трудно судить, насколько богатые возможности предоставляет этот механизм. В самом деле, если переопределение методов используется только для соблюдения соглашений о пространствах имен, то его можно рассматривать как любопытную, но почти бесполезную особенность языка программирования. Но это совсем не так. Переопределение методов лежит в основе одного из наиболее эффективных языковых средств Java: динамической диспетчеризации методов, представляющей собой механизм вызова переопределяемых методов, когда выбор конкретного метода осуществляется не на этапе компиляции, а в процессе выполнения программы. Динамическая диспетчеризация методов имеет очень большое значение, поскольку именно с ее помощью принцип полиморфизма реализуется на стадии выполнения программ на Java.