Что такое override в java
Перейти к содержимому

Что такое override в java

  • автор:

Переопределение методов

Если в иерархии классов совпадают имена и сигнатуры типов методов из подкласса и супер класса, то говорят, что метод из подкласса переопределяет метод из супер-класса.

Переопределение методов выполняется только в том случае, если имена и сигнатуры типов обоих методов одинаковы. В противном случае оба методы считаются перегружаемыми.

В следующем примере в классе M определен метод print() . В его наследнике классе N тоже определен метод print() с такой же сигнатурой, но другим поведением. Это и называется переопределением методов:

public class M < public int i; public int j; public M(int i, int j) < this.i = i; this.j = j; >public void print() < System.out.println("Метод M i = " + i + " j language-java">public class N extends M < public int k; public N(int i, int j, int k) < super(i, j); this.k = k; >public void print() < System.out.println("Метод N k language-java">public class OverrideDemo < public static void main(String[] args) < M obj1 = new M(7, 8); obj1.print(); N obj2 = new N(4, 5, 6); obj2.print(); M obj3 = new N(1, 2, 3); obj3.print(); >>
Метод M i = 7 j = 8 Метод N k = 6 Метод N k = 3

Существует такое понятие в Java как динамическая диспетчеризация методов - это механизм, с помощью которого вызов переопределенного метода разрешается во время выполнения, а не компиляции.

Переопределение методов это одна из форм реализации полиморфизма. Переопределение метода позволяет определить в общем классе методы, которые станут общими для всех производных от него классов, а в подклассах - конкретные реализации некоторых или всех этих методов.

Рассмотрим более конкретный пример, который показывает зачем переопределяются методы.

Создадим класс Figure , описывающий какую-то абстрактную фигуру и классы наследники Triangle и Rectangle . Класс Figure содержит метод calculateArea() , подсчитывающий площадь фигуры. У каждой фигуры своя формула для подсчета площади, поэтому в классах Triangle и Rectangle метод calculateArea() переопределяется соответствующим образом:

public class Figure < double dimension1; double dimension2; public Figure(double dimension1, double dimension2) < this.dimension1 = dimension1; this.dimension2 = dimension2; >public double calculateArea() < System.out.println("Площадь фигуры не определена."); return 0; >>
public class Rectangle extends Figure < public Rectangle(double dimension1, double dimension2) < super(dimension1, dimension2); >public double calculateArea() < System.out.println("B области четырехугольника."); return dimension1 * dimension2; >>
public class Triangle extends Figure < public Triangle(double dimension1, double dimension2) < super(dimension1, dimension2); >public double calculateArea() < System.out.println("B области треугольника."); return dimension1 * dimension2 / 2; >>

Создадим массив типа Figure , который будет содержать объекты типа Figure , Triangle и Rectangle . Подсчитаем площадь для каждого элемента перебирая элементы массива и вызывая метод calculateArea() для каждого элемента. Нам все равно какого типа объект - у каждого есть вызываемый метод calculateArea() . JVM с помощью динамической диспетчеризации выбирает нужный вариант метода, основываясь на реальном типе объекта:

public class FindAreas < public static void main(String[] args) < Figure[] figures = new Figure[3]; figures[0] = new Figure(10, 10); figures[1] = new Rectangle(10, 10); figures[2] = new Triangle(10, 10); for (Figure figure : figures) < double area = figure.calculateArea(); System.out.println(area); >> >

Результат выполнения кода:

Площадь фигуры не определена. 0.0 B области четырехугольника. 100.0 B области треугольника. 50.0

2. Методы подставки

После выхода Java 5 появилась возможность при переопределении методов указывать другой тип возвращаемого значения, в качестве которого можно использовать только типы, находящиеся ниже в иерархии наследования, чем исходный тип. Такие типы еще называются ковариантными.

Например, класс S наследует класс R и переопределяет метод getInstance() . При переопределении возвращаемый тип метода может или остаться таким же - Box6 , или быть изменен на любого наследника класса Box6 - HeavyBox , ColorBox или Shipment :

public class R < Box6 getInstance() < return new Box6(); >>
public class S extends R < HeavyBox getInstance() < return new HeavyBox(); >> 

3. Переопределение и статические методы

Статические методы не могут быть переопределены. Класс наследник может объявлять метод с такой же сигнатурой, что и супер класс, но это не будет переопределением. При вызове переопределенного метода JVM выбирает нужный вариант основываясь на типе объекта. Вызов же статического метода происходит без объекта. Версия вызываемого статического метода всегда определяется на этапе компиляции.

При использовании ссылки для доступа к статическому члену компилятор при выборе метода учитывает тип ссылки, а не тип объекта, ей присвоенного.

Создадим в супер классе и наследнике статические методы с одинаковой сигнатурой:

public class Base < public static void go() < System.out.println("метод из Base"); >>
public class Sub extends Base < public static void go() < System.out.println("метод из Sub"); >>

Попробуем вызвать статический метод используя переменную типа Base , которая указывает на объект типа Sub . При вызове статического метода JVM найдет тип переменной и вызовет метод того же класса:

public class Runner < public static void main(String[] args) < Base ob = new Sub(); ob.go(); Sub.go(); >>
метод из Base метод из Sub

4. Переопределение методов в классах наследниках

Методы объявленные как private никто, кроме самого класса не видит. Поэтому их наличие/отсутствие никак не отражается на классах наследниках. Они с легкостью могут объявлять методы с такой же сигнатурой и любыми модификаторами. Но это плохой тон! Также класс наследник может расширить видимость protected метода до public . Сузить видимость класс наследник не может.

5. Аннотация @Override

Необязательная аннотация @Override используется с методом для указания того, что он переопределен. Если метод переопределен неверно, код не будет компилироваться:

public class S extends R < @Override HeavyBox getInstance() < return new HeavyBox(); >>
  • Процедурное и объектно-ориентированное программирование
  • Принципы ООП
  • Классы и объекты
  • Конструктор
  • Ключевое слово this
  • Перегрузка
  • Стек и куча
  • Передача объектов в методы
  • Java varargs
  • Рекурсия
  • Сборщик мусора и метод finalize
  • Наследование
  • Ключевое слово super
  • Модификаторы доступа
  • Геттеры и сеттеры
  • Абстрактные классы и методы
  • Ключевое слово final
  • Задания

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 в Java?

Аннотация @Override в языке программирования Java используется для явного указания, что метод в подклассе должен переопределить метод с тем же именем и сигнатурой из его суперкласса. Это помогает обнаруживать ошибки в коде и повышает читаемость программы.

Основные моменты:

  1. Информационный характер:
    • @Override является аннотацией времени компиляции и не влияет на выполнение программы. Она служит скорее как подсказка компилятору и другим разработчикам.
  2. Предотвращение ошибок:
    • Если метод в подклассе помечен @Override , но не переопределяет ни один метод суперкласса, компилятор выдаст ошибку.
  3. Читаемость кода:
    • Использование @Override делает код более явным и понятным. Это особенно полезно при работе с большими кодовыми базами или при взаимодействии с кодом других разработчиков.
class Animal < void makeSound() < System.out.println("Some generic sound"); >> class Dog extends Animal < @Override void makeSound() < System.out.println("Woof! Woof!"); >void fetch() < System.out.println("Dog is fetching"); >>

Использование аннотации @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, который помогает улучшить читаемость и поддерживаемость кода, предотвращая возможные ошибки при переопределении методов.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *