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
Переопределение методов Java
Привет! Это статья про переопределение (override) методов в Java. Она тесно связана с пониманием принципов ООП, классов и механизма наследования. Если Вы плохо разбираетесь в этих темах, сначала почитайте:
Что такое переопределение в Java
Итак, Вы уже знакомы с понятиями классов и методов в Java. Наверняка Вам даже известно, что одни классы могут наследовать другие.
И вот в этот момент — момент наследования — может возникнуть проблема. Чтобы проиллюстрировать, представим, что у нас есть класс Animal. Этот класс имеет несколько методов — в том числе и метод voice() («голос»):
Давайте теперь представим, что мы создаем класс Cat, который наследует Animal:
Но сейчас если мы создадим кошку и запустим метод voice() , мы получим «This is my voice!». Например, если запустим это:
Как мы знаем, кошка не говорит «This is my voice!» 🙂 Кошка мяукает, собака лает, и т.д.
Таким образом, мы хотели бы, чтобы при вызове метода voice() наша кошка говорила «Мяу». Конечно, мы можем создать новый метод — например, catVoice(), — но это было бы не очень эффективно. Что, если мы хотим изменить 3, 5 или 10 методов? И что, метод родителя будет лежать у нас мертвым грузом?
Нет, нам совершенно не обязательно идти этим путем. Гораздо лучше, если мы просто будем иметь «свой вариант» нужного метода. Мы можем сделать так, чтобы нам не надо было менять название или параметры метода, но он вызвал нужный нам результат. Для этого мы просто должны переопределить метод родителя.
Как нам переопределить метод voice()? Все очень просто:
- Во-первых, мы заново прописываем метод voice() в нашем классе Cat
- Во-вторых, мы должны дать Java понять, что мы не ошиблись и не назвали метод названием, которое уже есть, из-за невнимательности. Чтобы сказать, что мы знаем, что делаем и действительно хотим переопределить(по англ. «override«) метод, нужно над методом написать @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, который помогает улучшить читаемость и поддерживаемость кода, предотвращая возможные ошибки при переопределении методов.