Переопределение метода в объектно-ориентированном программировании — это функция языка, которая позволяет подклассу или дочернему классу предоставлять конкретную реализацию метода , который уже предоставлен одним из его суперклассов или родительских классов. Помимо предоставления управляемых данными параметров, определяемых алгоритмом, через виртуальные сетевые интерфейсы, [1] он также позволяет использовать определенный тип полиморфизма ( подтипирование ). Реализация в подклассе переопределяет (заменяет) реализацию в суперклассе, предоставляя метод с тем же именем, теми же параметрами или сигнатурой и тем же типом возвращаемого значения, что и метод в родительском классе. [2] Версия выполняемого метода будет определяться объектом , который используется для его вызова. Если для вызова метода используется объект родительского класса, то будет выполнена версия в родительском классе, но если для вызова метода используется объект подкласса, тогда будет выполнена версия в дочернем классе. [3] Это помогает предотвратить проблемы, связанные с аналитикой дифференциальных реле, которая в противном случае полагалась бы на структуру, в которой можно было бы избежать переопределения метода. [4] [5] Некоторые языки позволяют программисту предотвратить переопределение метода.
По умолчанию Ada предоставляет переопределение методов. Чтобы способствовать раннему обнаружению ошибок (например, орфографических ошибок), можно указать, когда метод будет фактически переопределяться, а когда нет. Это будет проверено компилятором.
тип Т новый Управляется с помощью ......; процедура Op ( Obj : in out T ; Данные : in Integer ); тип NT — новый T с нулевой записью ; overriding — процедура переопределения индикатора Op ( Obj : in out NT ; Data : in Integer ); overriding — процедура переопределения индикатора Op ( Obj : in out NT ; Data : in String ); -- ^ компилятор выдает ошибку: подпрограмма "Op" не переопределяет
C# поддерживает переопределение метода, но только если это явно запрошено с использованием модификаторов override
и virtual
или abstract
.
абстрактный класс Animal { public string Name { get ; набор ; } // Методы public void Drink (); общественная виртуальная пустота Eat (); общественная пустота Go (); } класс Cat : Animal { public new string Name { get ; набор ; } // Методы public void Drink (); // Внимание: скрывает унаследованный напиток(). Использовать новое общедоступное переопределение void Eat (); // Переопределяет унаследованный метод eat(). общественный новый недействительный Go (); // Скрывает унаследованный go(). }
При переопределении одного метода другим сигнатуры двух методов должны быть идентичными (и с одинаковой видимостью). В C# методы классов , индексаторы , свойства и события могут быть переопределены.
Невиртуальные или статические методы не могут быть переопределены. Переопределенный базовый метод должен быть виртуальным , абстрактным или переопределяемым .
Помимо модификаторов, используемых для переопределения метода, C# позволяет скрывать унаследованное свойство или метод. Это делается с использованием той же сигнатуры свойства или метода, но с добавлением модификатора new
перед ней. [6]
В приведенном выше примере скрытие приводит к следующему:
Кот кот = новый Кот (); кот . Имя = … ; // обращается к Cat.Name cat . Есть (); // вызывает Cat.Eat() cat . Идти (); // вызывает Cat.Go() (( Animal ) cat ). Имя = … ; // обращается к Animal.Name! (( Животное ) кот ). Есть (); // вызывает Cat.Eat()! (( Животное ) кот ). Идти (); // вызывает Animal.Go()!
В C++ нет ключевого слова super
, которое подкласс может использовать в Java для вызова версии суперкласса метода, который он хочет переопределить. Вместо этого используется имя родительского или базового класса, за которым следует оператор разрешения области . Например, следующий код представляет два класса : базовый класс Rectangle
и производный класс Box
. Box
переопределяет метод Rectangle
класса Print
, чтобы также напечатать его высоту. [7]
#include <iostream> //------------------------------------------------ --------------------------- class Rectangle { public : Rectangle ( double l , double w ) : length_ ( l ), width_ ( w ) {} виртуальная пустота Print () const ; частный : двойная длина_ ; двойная ширина_ ; }; //------------------------------------------------ --------------------------- void Rectangle::Print () const { // Метод печати базового класса. std :: cout << "Длина = " << длина_ << "; Ширина = " << ширина_ ; } //------------------------------------------------ --------------------------- class Box : public Rectangle { public : Box ( double l , double w , double h ) : Rectangle ( l , w ), height_ ( h ) {} void Print () const override ; частный : двойная высота_ ; }; //------------------------------------------------ --------------------------- // Метод печати производного класса. void Box::Print () const { // Вызов родительского метода печати. Прямоугольник :: Печать (); std :: cout << "; Height = " << height_ ; }
Метод Print
в классе Box
, вызывая родительскую версию метода Print
, также может выводить частные переменные length
и width
базового класса. В противном случае эти переменные недоступны для Box
.
Следующие операторы будут создавать экземпляры объектов типа Rectangle
и Box
и вызывать их соответствующие Print
методы:
int main ( int argc , char ** argv ) { Прямоугольник ( 5.0 , 3.0 ) ; // Выходы: Длина = 5.0; Ширина = 3,0 прямоугольника . Распечатать (); Коробка ( 6.0 , 5.0 , 4.0 ) ; // Указатель на самый переопределенный метод в vtable в Box::print, // но этот вызов не иллюстрирует переопределение. коробка . Распечатать (); // Этот вызов иллюстрирует переопределение. // выходные данные: Длина = 6.0; Ширина = 5,0; Высота = 4,0 static_cast < Прямоугольник &> ( коробка ). Распечатать (); }
В C++11 , как и в Java, метод, объявленный final
в суперклассе, не может быть переопределен; Кроме того, можно объявить метод override
, чтобы компилятор проверил, переопределяет ли он метод базового класса.
В Delphi переопределение метода выполняется с помощью директивы override , но только в том случае, если метод был помечен динамическими или виртуальными директивами .
Унаследованное зарезервированное слово должно вызываться, когда вы хотите вызвать поведение суперкласса .
тип TRectangle = частный класс FLength : Double ; FWidth : Двойной ; общедоступное свойство Длина чтения FLength write FLength ; свойство Width чтение FWidth запись FWidth ; процедура Печать ; виртуальный ; конец ; TBox = класс ( TRectangle ) общедоступная процедура Print ; переопределить ; конец ;
В Eiffel переопределение функции аналогично переопределению метода в C++ и Java. Переопределение — это одна из трех форм адаптации функций, классифицируемых как переобъявление . Повторное объявление также охватывает effecting , при котором предоставляется реализация функции, которая была отложена (абстрактной) в родительском классе, и undefinition , при которой функция, которая была эффективной (конкретной) в родительском классе, снова становится отложенной в классе-наследнике. Когда функция переопределяется, имя функции сохраняется в классе-наследнике, но свойства функции, такие как ее подпись, контракт (с учетом ограничений для предусловий и постусловий ) и/или реализация, будут отличаться в наследнике. Если исходная функция в родительском классе, называемая предшественником функции-наследника , эффективна, то будет эффективна и переопределенная функция в наследнике. Если предшественник откладывается, функция наследника будет отложена. [8]
Намерение переопределить функцию, как message
в примере ниже, должно быть явно объявлено в inherit
предложении класса-наследника.
сообщение функции класса THOUGHT - Отображение мыслительного сообщения do print ( "Я чувствую, что припаркован по диагонали в параллельной вселенной.%N" ) end end class ADVICE inherit THOUGHT redefine message end Feature message -- Precursor do print ( "Предупреждение: даты в календаре ближе, чем кажутся.%N" ) end end
В классе ADVICE
функция message
получает реализацию, отличную от реализации ее предшественника в классе THOUGHT
.
Рассмотрим класс, который использует экземпляры для обоих THOUGHT
и ADVICE
:
class APPLICATION create make Feature make -- Запустить приложение. сделать ( создать { МЫСЛЬ }). сообщение ; ( создать { СОВЕТ }). конец сообщения конец
При создании экземпляра класс APPLICATION
выдает следующий результат:
У меня такое ощущение, будто я припарковался по диагонали в параллельной вселенной. Внимание: Даты в календаре ближе, чем кажутся.
В пределах переопределенной функции доступ к ее предшественнику можно получить с помощью ключевого слова языка Precursor
. Предположим, что реализация изменена следующим образом:{ADVICE}.message
message -- Precursor do print ( "Предупреждение: даты в календаре ближе, чем кажутся.%N" ) Конец предшественника
Вызов этой функции теперь включает в себя выполнение и выдает следующий результат:{THOUGHT}.message
Внимание: Даты в календаре ближе, чем кажутся. У меня такое ощущение, будто я припарковался по диагонали в параллельной вселенной.
В Java , когда подкласс содержит метод с той же сигнатурой (имя и типы параметров), что и метод в его суперклассе, тогда метод подкласса переопределяет метод суперкласса. Например:
класс Мысль { общественное недействительное сообщение () { System . вне . println ( "Я чувствую себя так, будто нахожусь по диагонали в параллельной вселенной." ); } } public class Advice расширяет Мысль { @Override // Аннотация @Override в Java 5 необязательна, но полезна. общественное недействительное сообщение () { System . вне . println ( "Внимание: даты в календаре ближе, чем кажутся." ); } }
Класс Thought
представляет суперкласс и реализует вызов метода . Вызванный подкласс наследует каждый метод, который может быть в классе. Класс переопределяет метод , заменяя его функциональность из .message()
Advice
Thought
Advice
message()
Thought
Мысль парковка = новая мысль (); парковка . сообщение (); // Выводит «У меня такое ощущение, будто я припарковался по диагонали в параллельной вселенной». Даты мысли = новый совет (); // Даты полиморфизма . сообщение (); // Печатает «Внимание: даты в календаре ближе, чем кажутся».
Когда подкласс содержит метод, который переопределяет метод суперкласса, то этот переопределенный метод (суперкласса) может быть явно вызван из метода подкласса с помощью ключевого слова super
. [3] (Его нельзя явно вызвать из какого-либо метода, принадлежащего классу, не связанному с суперклассом.) Ссылка super
может быть
совет общественного класса расширяет мысль { @Override public void message () { System . вне . println ( "Внимание: даты в календаре ближе, чем кажутся." ); супер . сообщение (); // Вызов родительской версии метода. }
Существуют методы, которые подкласс не может переопределить. Например, в Java метод, объявленный как Final в суперклассе, не может быть переопределен. Методы, объявленные частными или статическими, также не могут быть переопределены, поскольку они неявно являются окончательными. Класс, объявленный как Final, также не может стать суперклассом. [9]
В Kotlin мы можем просто переопределить такую функцию (обратите внимание, что функция должна быть open
):
fun main () { val p = Родитель ( 5 ) val c = Дочерний ( 6 ) p . myFun () c . myFun () } открытый класс Parent ( val a : Int ) { open fun myFun () = println ( a ) } class Child ( val b : Int ) : Parent ( b ) { переопределить fun myFun () = println ( «переопределенный метод» ) }
В Python , когда подкласс содержит метод, который переопределяет метод суперкласса, вы также можете вызвать метод суперкласса, вызвав [10] вместо . Пример:super(Subclass, self).method
self.method
class Thought : def __init__ ( self ) -> None : print ( «Я новый объект типа Thought!» ) def message ( self ) -> None : print ( «Я чувствую себя так, словно припаркован по диагонали в параллельной вселенной ." )класс Совет ( Мысль ): def __init__ ( self ) -> Нет : супер ( Совет , self ) . __init__ () def message ( self ) -> None : print ( «Внимание: даты в календаре ближе, чем кажутся» ) super ( Совет , self ) . сообщение ()t = Мысль () # "Я новый объект типа Мысль!" т . message () # «У меня такое ощущение, будто я припаркован по диагонали в параллельной вселенной.a = Advice () # "Я новый объект типа Мысль!" а . message () # "Внимание: даты в календаре ближе, чем кажутся" # "У меня такое ощущение, будто я припаркован по диагонали в параллельной вселенной.# ------------------ # Самоанализ:isinstance ( t , Thought ) # Правдаisinstance ( a , Совет ) # Верноisinstance ( a , Thought ) # Правда
В Ruby , когда подкласс содержит метод, который переопределяет метод суперкласса, вы также можете вызвать метод суперкласса, вызвав super в этом переопределенном методе. Вы можете использовать псевдоним, если хотите, чтобы переопределенный метод был доступен за пределами переопределяющего метода, как показано в «super_message» ниже.
Пример:
Сообщение class Thought def гласит : «Я чувствую, что нахожусь по диагонали в параллельной вселенной». конец конец class Advice < Псевдоним мысли :super_message :message def message помещает «Предупреждение: даты в календаре ближе, чем кажутся» super end end
super().method