24. Java – Переопределение (overriding)
В предыдущей главе мы рассказали про суперклассы и подклассы. Если класс наследует метод из своего суперкласса, тогда есть шанс переопределить взятый метод, если он не помечен final.
Так что такое в Java overriding или override – это переопределение.
Преимущество в Java переопределения заключается в том, что оно позволяет определять (описывать) поведение, характерное для типа подкласса, значит подкласс может реализовать метод родительского класса на основе его требования.
В объектно-ориентированных терминах, переопределение значит перезапись функционала существующего метода.
Пример 1
class Animal < public void move() < System.out.println("Животные могут двигаться"); >> class Dog extends Animal < public void move() < System.out.println("Собаки могут ходить и бегать"); >> public class TestDog < public static void main(String args[]) < Animal a = new Animal(); // Animal - ссылка и объект Animal b = new Dog(); // Animal - ссылка, но Dog - объект a.move(); // Запускает метод в классе Animal b.move(); // Запускает метод в классе Dog >>
После запуска программы будет выдан такой результат:
Животные могут двигаться Собаки могут ходить и бегать
В вышеприведённом примере вы можете заметить, что b хоть и является типом Animal, оно запускает метод move в классе Dog. Причина тому: во время компиляции проходит проверка ссылочного типа. Однако, во время выполнения, JVM определяет тип объекта и запускает метод, который принадлежит этому конкретному объекту.
Следовательно, по примеру выше, программа запустится правильно, так как класс Animal имеет метод move. Затем, во время выполнения, он запускает метод, принадлежащий этому объекту.
Рассмотрите следующий пример:
Пример 2
class Animal < public void move() < System.out.println("Животные могут двигаться"); >> class Dog extends Animal < public void move() < System.out.println("Собаки могут ходить и бегать"); >public void bark() < System.out.println("Собаки могут лаять"); >> public class TestDog < public static void main(String args[]) < Animal a = new Animal(); // Animal - ссылка и объект Animal b = new Dog(); // Animal - ссылка, но Dog - объект a.move(); // Запускает метод в классе Animal b.move(); // Запускает метод в классе Dog b.bark(); >>
После запуска программы будет выдан такой результат:
TestDog.java:26: error: cannot find symbol b.bark(); ^ symbol: method bark() location: variable b of type Animal 1 error
Программа выдаст ошибку во время компиляции, так как ссылочный тип b у Animal не имеет метода под именем bark.
Правила переопределения метода
- Список аргументов должен быть точно таким же, как и для переопределённого метода.
- Возвращаемый тип должен быть таким же или подтипом возвращаемого типа, объявленного в исходном переопределенном методе в суперклассе.
- Уровень доступа не может быть более ограниченным, чем уровень доступа переопределённого метода. Например, если метод суперкласса объявлен public, то переопределяемый метод в подклассе не может быть private или protected.
- Методы экземпляров могут быть переопределены только если они наследованы подклассом.
- Методы, которые объявлены как final, не могут быть переопределены.
- Статические методы, которые объявлены как static, не могут быть переопределены, но могут быть повторно объявлены.
- Если метод нельзя наследовать, то его нельзя переопределить.
- Подкласс внутри того же пакета, что и суперкласс экземпляра, может переопределять любой метод суперкласса, который не объявлен как private или final.
- Подкласс в другом пакете может переопределять только не final методы, объявленные как public или protected.
- Переопределяемый метод может выдавать любые непроверенные исключения вне зависимости от того, переопределяет ли переопределённый метод какие-либо непроверенные исключения или нет. Однако, переопределяемый метод не должен генерировать проверенные исключения, которые являются новыми или более широкими, чем те, которые объявлены переопределённым методом. Переопределенный метод может генерировать более узкие или меньшие исключения, чем переопределенный метод.
- Конструкторы нельзя переопределить.
Использование ключевого слова super
Вызывая версию суперкласса переопределённого метода, используется ключевое слово super.
Пример
class Animal < public void move() < System.out.println("Животные могут двигаться"); >> class Dog extends Animal < public void move() < super.move(); // Вызывает метод суперкласса System.out.println("Собаки могут ходить и бегать"); >> public class TestDog < public static void main(String args[]) < Animal b = new Dog(); // Animal - ссылка, но Dog - объект b.move(); // Запуск метода в классе Dog >>
После запуска программы будет выдан такой результат:
Животные могут двигаться Собаки могут ходить и бегать
Оглавление
- 1. Java – Самоучитель для начинающих
- 2. Java – Обзор языка
- 3. Java – Установка и настройка
- 4. Java – Синтаксис
- 5. Java – Классы и объекты
- 6. Java – Конструкторы
- 7. Java – Типы данных и литералы
- 8. Java – Типы переменных
- 9. Java – Модификаторы
- 10. Java – Операторы
- 11. Java – Циклы и операторы цикла
- 11.1. Java – Цикл while
- 11.2. Java – Цикл for
- 11.3. Java – Улучшенный цикл for
- 11.4. Java – Цикл do..while
- 11.5. Java – Оператор break
- 11.6. Java – Оператор continue
- 12. Java – Операторы принятия решений
- 12.1. Java – Оператор if
- 12.2. Java – Оператор if..else
- 12.3. Java – Вложенный оператор if
- 12.4. Java – Оператор switch..case
- 12.5. Java – Условный оператор (? 🙂
- 13. Java – Числа
- 13.1. Java – Методы byteValue(), shortValue(), intValue(), longValue(), floatValue(), doubleValue()
- 13.2. Java – Метод compareTo()
- 13.3. Java – Метод equals()
- 13.4. Java – Метод valueOf()
- 13.5. Java – Метод toString()
- 13.6. Java – Метод parseInt()
- 13.7. Java – Метод Math.abs()
- 13.8. Java – Метод Math.ceil()
- 13.9. Java – Метод Math.floor()
- 13.10. Java – Метод Math.rint()
- 13.11. Java – Метод Math.round()
- 13.12. Java – Метод Math.min()
- 13.13. Java – Метод Math.max()
- 13.14. Java – Метод Math.exp()
- 13.15. Java – Метод Math.log()
- 13.16. Java – Метод Math.pow()
- 13.17. Java – Метод Math.sqrt()
- 13.18. Java – Метод Math.sin()
- 13.19. Java – Метод Math.cos()
- 13.20. Java – Метод Math.tan()
- 13.21. Java – Метод Math.asin()
- 13.22. Java – Метод Math.acos()
- 13.23. Java – Метод Math.atan()
- 13.24. Java – Метод Math.atan2()
- 13.25. Java – Метод Math.toDegrees()
- 13.26. Java – Метод Math.toRadians()
- 13.27. Java – Метод Math.random()
- 14. Java – Символы
- 14.1. Java – Метод Character.isLetter()
- 14.2. Java – Метод Character.isDigit()
- 14.3. Java – Метод Character.isWhitespace()
- 14.4. Java – Метод Character.isUpperCase()
- 14.5. Java – Метод Character.isLowerCase()
- 14.6. Java – Метод Character.toUpperCase()
- 14.7. Java – Метод Character.toLowerCase()
- 14.8. Java – Метод Character.toString()
- 15. Java – Строки
- 15.1. Java – Метод charAt()
- 15.2. Java – Метод compareTo()
- 15.3. Java – Метод compareToIgnoreCase()
- 15.4. Java – Метод concat()
- 15.5. Java – Метод contentEquals()
- 15.6. Java – Метод copyValueOf()
- 15.7. Java – Метод endsWith()
- 15.8. Java – Метод equals()
- 15.9. Java – Метод equalsIgnoreCase()
- 15.10. Java – Метод getBytes()
- 15.11. Java – Метод getChars()
- 15.12. Java – Метод hashCode()
- 15.13. Java – Метод indexOf()
- 15.14. Java – Метод intern()
- 15.15. Java – Метод lastIndexOf()
- 15.16. Java – Метод length()
- 15.17. Java – Метод matches()
- 15.18. Java – Метод regionMatches()
- 15.19. Java – Метод replace()
- 15.20. Java – Метод replaceAll()
- 15.21. Java – Метод replaceFirst()
- 15.22. Java – Метод split()
- 15.23. Java – Метод startsWith()
- 15.24. Java – Метод subSequence()
- 15.25. Java – Метод substring()
- 15.26. Java – Метод toCharArray()
- 15.27. Java – Метод toLowerCase()
- 15.28. Java – Метод toString()
- 15.29. Java – Метод toUpperCase()
- 15.30. Java – Метод trim()
- 15.31. Java – Метод valueOf()
- 15.32. Java – Классы StringBuilder и StringBuffer
- 15.32.1. Java – Метод append()
- 15.32.2. Java – Метод reverse()
- 15.32.3. Java – Метод delete()
- 15.32.4. Java – Метод insert()
- 15.32.5. Java – Метод replace()
- 16. Java – Массивы
- 17. Java – Дата и время
- 18. Java – Регулярные выражения
- 19. Java – Методы
- 20. Java – Потоки ввода/вывода, файлы и каталоги
- 20.1. Java – Класс ByteArrayInputStream
- 20.2. Java – Класс DataInputStream
- 20.3. Java – Класс ByteArrayOutputStream
- 20.4. Java – Класс DataOutputStream
- 20.5. Java – Класс File
- 20.6. Java – Класс FileReader
- 20.7. Java – Класс FileWriter
- 21. Java – Исключения
- 21.1. Java – Встроенные исключения
- 22. Java – Вложенные и внутренние классы
- 23. Java – Наследование
- 24. Java – Переопределение
- 25. Java – Полиморфизм
- 26. Java – Абстракция
- 27. Java – Инкапсуляция
- 28. Java – Интерфейсы
- 29. Java – Пакеты
- 30. Java – Структуры данных
- 30.1. Java – Интерфейс Enumeration
- 30.2. Java – Класс BitSet
- 30.3. Java – Класс Vector
- 30.4. Java – Класс Stack
- 30.5. Java – Класс Dictionary
- 30.6. Java – Класс Hashtable
- 30.7. Java – Класс Properties
- 31. Java – Коллекции
- 31.1. Java – Интерфейс Collection
- 31.2. Java – Интерфейс List
- 31.3. Java – Интерфейс Set
- 31.4. Java – Интерфейс SortedSet
- 31.5. Java – Интерфейс Map
- 31.6. Java – Интерфейс Map.Entry
- 31.7. Java – Интерфейс SortedMap
- 31.8. Java – Класс LinkedList
- 31.9. Java – Класс ArrayList
- 31.10. Java – Класс HashSet
- 31.11. Java – Класс LinkedHashSet
- 31.12. Java – Класс TreeSet
- 31.13. Java – Класс HashMap
- 31.14. Java – Класс TreeMap
- 31.15. Java – Класс WeakHashMap
- 31.16. Java – Класс LinkedHashMap
- 31.17. Java – Класс IdentityHashMap
- 31.18. Java – Алгоритмы Collection
- 31.19. Java – Iterator и ListIterator
- 31.20. Java – Comparator
- 32. Java – Дженерики
- 33. Java – Сериализация
- 34. Java – Сеть
- 34.1. Java – Обработка URL
- 35. Java – Отправка Email
- 36. Java – Многопоточность
- 36.1. Java – Синхронизация потоков
- 36.2. Java – Межпоточная связь
- 36.3. Java – Взаимная блокировка потоков
- 36.4. Java – Управление потоками
- 37. Java – Основы работы с апплетами
- 38. Java – Javadoc
Что означает «@Override» перед объявлением метода?
На самом деле аннотация @Override указывает, что далее мы собираемся переопределять метод базового класса.
При этом, если в базовом классе не окажется метода с аналогичной сигнатурой, то мы получим предупреждение компилятора о том, что хотя мы и собирались что-то переопределить, по факту этого не произошло.
На этом действие аннотации заканчивается.
Таким образом, аннотация никак не влияет на сам факт переопределения метода — при совпадении сигнатур с методом базового класса он и так будет переопределен, независимо от наличия, либо отсутствия этой аннотации. Аннотация служит лишь для контроля успешности действия при сборке проекта.
В случае отсутствия данной аннотации и не совпадении сигнатур (в результате ошибки) с методом, который мы собирались переопределять — случится страшное — очень трудноуловимая ошибка, когда вы думаете, что должно бы переопределяться, а по факту имеете отдельный независимый метод, который скорее всего вообще не выполняется. При этом компилятор считает, что с его точки зрения все в порядке — хотите собственный метод в классе-наследнике — нет проблем. И молчит.
Зачем и где именно писать @Override?
Это аннотация пишется над методами, которые переопределяются из супер класса.
Это что-то вроде страховки, гарантирующая что 100% переопределен метод.
class A < public void superMethod() <>> class B extends A < @Override public void superMethod() <>>
Компилятор, когда встретит данную аннотацию, проверит, чтоб сигнатуры методов в супер классе и в под классе совпадали, т.е. вы переопределили метод, и в полиморфизме будит вызываться переопределенный метод.
Если вдруг вы ошиблись в сигнатуре метода (например пропустили букву в название), то компилятор выдаст ошибку
Ответ написан более трёх лет назад
Нравится 15 2 комментария

Показателен пример с переопределением Object.equals(Object) — люди часто ошибаются с сигнатурой: class MyClass < public boolean equals(MyClass o) < /* blah-blah */ >>. В итоге определили новый метод, а не переопределили родительский. Причём ошибка может не сразу обнаружиться.
Ладно equals() можно запомнить очень быстро. Базовый метод, как-никак. Часто на слуху. Но в нетривиальных случаях можно неплохо подпортить себе нервы, пытаясь отдебажить проблему, которую можно было переложить на компилятор аннотацией @Override.
Использование аннотации @Override в Java
Аннотация @Override используется в Java для указания того, что поданнотированный метод является переопределенным методом из суперкласса. Это служит предупреждением для компилятора, чтобы он проверил, является ли этот метод действительно переопределением.
Рассмотрим пример. Имеется суперкласс Animal с методом makeSound(), и подкласс Dog, который также имеет метод makeSound(). Если метод в подклассе Dog должен переопределить метод из суперкласса Animal, он должен быть поданнотирован аннотацией @Override.
class Animal < void makeSound() < System.out.println("The animal makes a sound"); >> class Dog extends Animal < @Override void makeSound() < System.out.println("The dog barks"); >>
Здесь аннотация @Override говорит компилятору, что метод makeSound() в классе Dog предназначен для переопределения метода из суперкласса. Если такого метода в суперклассе нет, компилятор выдаст ошибку.
Теперь, когда стало понятно, что такое @Override и для чего она используется, можно перейти к вопросу о том, когда её стоит использовать.
Вообще говоря, хорошей практикой считается использование @Override каждый раз, когда метод предназначен для переопределения метода из суперкласса. Это помогает предотвратить ошибки, особенно в больших и сложных проектах, где легко забыть или пропустить переопределение.
Однако есть исключения. Например, если метод из интерфейса реализуется в классе, то использование @Override не является обязательным, поскольку все методы интерфейса по умолчанию являются абстрактными и должны быть реализованы в классе.
В заключение, можно сказать, что использование @Override — это важный элемент «чистого кода» в Java, который помогает улучшить читаемость и поддерживаемость кода, предотвращая возможные ошибки при переопределении методов.
