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