stringtranslate.com

Метод переопределения

Иллюстрация

Переопределение метода в объектно-ориентированном программировании — это языковая функция, которая позволяет подклассу или дочернему классу предоставлять определенную реализацию метода , который уже предоставляется одним из его суперклассов или родительских классов. Помимо предоставления управляемых данными параметров, определяемых алгоритмом, через виртуальные сетевые интерфейсы, [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()AdviceThoughtAdvicemessage()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).methodself.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            

Примечания

  1. ^ Чжан, Цзе (2015). «Новый P2P-переопределенный API для открытых коммуникаций данных в WWW». Международная конференция IEEE по потребительской электронике 2015 г. — Тайвань . С. 156–157. doi :10.1109/ICCE-TW.2015.7216830. ISBN 978-1-4799-8745-0. S2CID  23295793.
  2. ^ Фланаган 2002, стр. 107
  3. ^ Льюис и Лофтус 2006, стр.454
  4. ^ Overbey, J (2011). «Дифференциальная проверка предусловий: легкий, повторно используемый анализ для инструментов рефакторинга». 2011 26-я Международная конференция IEEE/ACM по автоматизированной разработке программного обеспечения (ASE 2011) . стр. 303–312. doi :10.1109/ASE.2011.6100067. ISBN 978-1-4577-1639-3. S2CID  5933208.
  5. ^ Ли, К (2014). «Остаточное расследование: прогнозируемое и точное обнаружение ошибок». Труды ACM по программной инженерии и методологии . 24 (2). doi : 10.1145/2656201. S2CID  47112802.
  6. ^ Мессенбёк, Ханспетер (25 марта 2002 г.). «Продвинутый C#: переопределение методов» (PDF) . Институт системного программного обеспечения, Университет Иоганна Кеплера в Линце, Факультет информатики. стр. 6–8 . Проверено 2 августа 2011 г.
  7. ^ Малик 2006, стр. 676
  8. ^ Мейер 2009, стр. 572-575
  9. ^ Дейтел и Дейтел 2001, стр.474
  10. ^ в Python 3 - см. https://docs.python.org/3/library/functions.html#super Архивировано 26.10.2018 на Wayback Machinesuper().method

Смотрите также

Ссылки

Внешние ссылки