Переопределение метода
Переопределение метода в объектно-ориентированном программировании — это функция языка, которая позволяет подклассу или дочернему классу предоставлять конкретную реализацию метода , который уже предоставлен одним из его суперклассов или родительских классов. Помимо предоставления управляемых данными параметров, определяемых алгоритмом, через виртуальные сетевые интерфейсы, [1] он также допускает определенный тип полиморфизма ( подтипирование ). Реализация в подклассе переопределяет (заменяет) реализацию в суперклассе, предоставляя метод с тем же именем, теми же параметрами или сигнатурой и тем же типом возвращаемого значения, что и метод в родительском классе. [2] Версия выполняемого метода будет определяться объектом , который используется для его вызова. Если для вызова метода используется объект родительского класса, то будет выполнена версия в родительском классе, но если для вызова метода используется объект подкласса, то будет выполнена версия в дочернем классе. [3] Это помогает предотвратить проблемы, связанные с аналитикой дифференциальных реле, которая в противном случае опиралась бы на структуру, в которой можно было бы избежать переопределения метода. [4] [5] Некоторые языки позволяют программисту предотвратить переопределение метода.
Примеры для конкретного языка
[ редактировать ]Есть
[ редактировать ]Ada По умолчанию предоставляет переопределение методов. Чтобы способствовать раннему обнаружению ошибок (например, орфографических ошибок), можно указать, когда метод ожидается, что оно будет фактически иметь приоритетное значение или нет. Это будет проверено компилятором.
type T is new Controlled with ......;
procedure Op(Obj: in out T; Data: in Integer);
type NT is new T with null record;
overriding -- overriding indicator
procedure Op(Obj: in out NT; Data: in Integer);
overriding -- overriding indicator
procedure Op(Obj: in out NT; Data: in String);
-- ^ compiler issues an error: subprogram "Op" is not overriding
С#
[ редактировать ]C# поддерживает переопределение метода, но только если это явно запрошено с использованием модификаторов. override
и virtual
или abstract
.
abstract class Animal
{
public string Name { get; set; }
// Methods
public void Drink();
public virtual void Eat();
public void Go();
}
class Cat : Animal
{
public new string Name { get; set; }
// Methods
public void Drink(); // Warning: hides inherited drink(). Use new
public override void Eat(); // Overrides inherited eat().
public new void Go(); // Hides inherited go().
}
При переопределении одного метода другим сигнатуры двух методов должны быть идентичными (и с одинаковой видимостью). В C# методы классов , индексаторы , свойства и события могут быть переопределены.
Невиртуальные или статические методы не могут быть переопределены. Переопределенный базовый метод должен быть виртуальным , абстрактным или переопределяемым .
Помимо модификаторов, используемых для переопределения метода, C# позволяет скрывать унаследованное свойство или метод. Это делается с использованием той же сигнатуры свойства или метода, но с добавлением модификатора new
перед этим. [6]
В приведенном выше примере скрытие приводит к следующему:
Cat cat = new Cat();
cat.Name = …; // accesses Cat.Name
cat.Eat(); // calls Cat.Eat()
cat.Go(); // calls Cat.Go()
((Animal)cat).Name = …; // accesses Animal.Name!
((Animal)cat).Eat(); // calls Cat.Eat()!
((Animal)cat).Go(); // calls 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;
private:
double length_;
double width_;
};
//---------------------------------------------------------------------------
void Rectangle::Print() const {
// Print method of base class.
std::cout << "Length = " << length_ << "; Width = " << width_;
}
//---------------------------------------------------------------------------
class Box : public Rectangle {
public:
Box(double l, double w, double h) : Rectangle(l, w), height_(h) {}
void Print() const override;
private:
double height_;
};
//---------------------------------------------------------------------------
// Print method of derived class.
void Box::Print() const {
// Invoke parent Print method.
Rectangle::Print();
std::cout << "; Height = " << height_;
}
Метод Print
в классе Box
, вызывая родительскую версию метода Print
, также может выводить частные переменные length
и width
базового класса. В противном случае эти переменные будут недоступны Box
.
Следующие инструкции будут создавать экземпляры объектов типа Rectangle
и Box
и позвоните соответствующим Print
методы:
int main(int argc, char** argv) {
Rectangle rectangle(5.0, 3.0);
// Outputs: Length = 5.0; Width = 3.0
rectangle.Print();
Box box(6.0, 5.0, 4.0);
// The pointer to the most overridden method in the vtable in on Box::print,
// but this call does not illustrate overriding.
box.Print();
// This call illustrates overriding.
// outputs: Length = 6.0; Width = 5.0; Height= 4.0
static_cast<Rectangle&>(box).Print();
}
В C++11 , как и в Java, метод, объявленный final
в суперклассе не может быть переопределен; также можно объявить метод override
чтобы компилятор проверил, что он переопределяет метод базового класса.
Дельфи
[ редактировать ]В Delphi переопределение метода выполняется с помощью директивы override , но только в том случае, если метод был помечен динамическими или виртуальными директивами .
Унаследованное зарезервированное слово должно вызываться , когда вы хотите вызвать поведение суперкласса.
type
TRectangle = class
private
FLength: Double;
FWidth: Double;
public
property Length read FLength write FLength;
property Width read FWidth write FWidth;
procedure Print; virtual;
end;
TBox = class(TRectangle)
public
procedure Print; override;
end;
Эйфелева
[ редактировать ]В Eiffel переопределение функции аналогично переопределению метода в C++ и Java. Переопределение — это одна из трех форм адаптации функций, классифицируемых как переобъявление . Повторное объявление также охватывает effecting , при котором предоставляется реализация функции, которая была отложена (абстрактной) в родительском классе, и undefinition , при которой функция, которая была эффективной (конкретной) в родительском классе, снова становится отложенной в классе-наследнике. Когда функция переопределяется, имя функции сохраняется в классе-наследнике, но свойства функции, такие как ее подпись, контракт (с учетом ограничений для предварительных и постусловий ) и/или реализация, будут отличаться в наследнике. функции-наследника Если исходная функция в родительском классе, называемая предшественником , эффективна, то будет эффективна и переопределенная функция в наследнике. Если предшественник откладывается, функция наследника будет отложена. [8]
Намерение переопределить признак, т. message
в примере ниже, должен быть явно объявлен в inherit
предложение класса наследника.
class
THOUGHT
feature
message
-- Display thought message
do
print ("I feel like I am diagonally parked in a parallel universe.%N")
end
end
class
ADVICE
inherit
THOUGHT
redefine
message
end
feature
message
-- Precursor
do
print ("Warning: Dates in calendar are closer than they appear.%N")
end
end
В классе ADVICE
особенность message
получает реализацию, которая отличается от реализации своего предшественника в классе THOUGHT
.
Рассмотрим класс, который использует экземпляры для обоих THOUGHT
и ADVICE
:
class
APPLICATION
create
make
feature
make
-- Run application.
do
(create {THOUGHT}).message;
(create {ADVICE}).message
end
end
При создании экземпляра класс APPLICATION
производит следующий вывод:
I feel like I am diagonally parked in a parallel universe.
Warning: Dates in calendar are closer than they appear.
В пределах переопределенной функции доступ к ее предшественнику можно получить, используя ключевое слово языка. Precursor
. Предположим, что реализация {ADVICE}.message
изменяется следующим образом:
message
-- Precursor
do
print ("Warning: Dates in calendar are closer than they appear.%N")
Precursor
end
Вызов этой функции теперь включает в себя выполнение {THOUGHT}.message
и выдает следующий результат:
Warning: Dates in calendar are closer than they appear.
I feel like I am diagonally parked in a parallel universe.
Ява
[ редактировать ]В Java , когда подкласс содержит метод с той же сигнатурой (имя и типы параметров), что и метод в его суперклассе, тогда метод подкласса переопределяет метод суперкласса. Например:
class Thought {
public void message() {
System.out.println("I feel like I am diagonally parked in a parallel universe.");
}
}
public class Advice extends Thought {
@Override // @Override annotation in Java 5 is optional but helpful.
public void message() {
System.out.println("Warning: Dates in calendar are closer than they appear.");
}
}
Сорт Thought
представляет суперкласс и реализует вызов метода message()
. Подкласс под названием Advice
наследует каждый метод, который может быть в Thought
сорт. Сорт Advice
переопределяет метод message()
, заменив его функциональность с Thought
.
Thought parking = new Thought();
parking.message(); // Prints "I feel like I am diagonally parked in a parallel universe."
Thought dates = new Advice(); // Polymorphism
dates.message(); // Prints "Warning: Dates in calendar are closer than they appear."
Когда подкласс содержит метод, который переопределяет метод суперкласса, тогда этот переопределенный метод (суперкласса) может быть явно вызван из метода подкласса с помощью ключевого слова super
. [3] (Его нельзя явно вызвать из какого-либо метода, принадлежащего классу, не связанному с суперклассом.)
super
ссылка может быть
public class Advice extends Thought {
@Override
public void message() {
System.out.println("Warning: Dates in calendar are closer than they appear.");
super.message(); // Invoke parent's version of method.
}
Существуют методы, которые подкласс не может переопределить. Например, в Java метод, объявленный как Final в суперклассе, не может быть переопределен. Методы, объявленные частными или статическими, также не могут быть переопределены, поскольку они неявно являются окончательными. Класс, объявленный как Final, также не может стать суперклассом. [9]
Котлин
[ редактировать ]В Kotlin мы можем просто переопределить такую функцию (обратите внимание, что функция должна быть open
):
fun main() {
val p = Parent(5)
val c = Child(6)
p.myFun()
c.myFun()
}
open class Parent(val a : Int) {
open fun myFun() = println(a)
}
class Child(val b : Int) : Parent(b) {
override fun myFun() = println("overrided method")
}
Питон
[ редактировать ]В Python , когда подкласс содержит метод, который переопределяет метод суперкласса, вы также можете вызвать метод суперкласса, вызвав super(Subclass, self).method
[10] вместо self.method
.
Пример:
class Thought:
def __init__(self) -> None:
print("I'm a new object of type Thought!")
def message(self) -> None:
print("I feel like I am diagonally parked in a parallel universe.")
class Advice(Thought):
def __init__(self) -> None:
super(Advice, self).__init__()
def message(self) -> None:
print("Warning: Dates in calendar are closer than they appear")
super(Advice, self).message()
t = Thought()
# "I'm a new object of type Thought!"
t.message()
# "I feel like I am diagonally parked in a parallel universe.
a = Advice()
# "I'm a new object of type Thought!"
a.message()
# "Warning: Dates in calendar are closer than they appear"
# "I feel like I am diagonally parked in a parallel universe.
# ------------------
# Introspection:
isinstance(t, Thought)
# True
isinstance(a, Advice)
# True
isinstance(a, Thought)
# True
Руби
[ редактировать ]В Ruby , когда подкласс содержит метод, который переопределяет метод суперкласса, вы также можете вызвать метод суперкласса, вызвав super в этом переопределенном методе. Вы можете использовать псевдоним, если хотите, чтобы переопределенный метод был доступен за пределами переопределяющего метода, как показано в «super_message» ниже.
Пример:
class Thought
def message
puts "I feel like I am diagonally parked in a parallel universe."
end
end
class Advice < Thought
alias :super_message :message
def message
puts "Warning: Dates in calendar are closer than they appear"
super
end
end
Примечания
[ редактировать ]- ^ Чжан, Цзе (2015). «Новый переопределенный API P2P для открытой передачи данных в WWW». Международная конференция IEEE по бытовой электронике, 2015 г., Тайвань . стр. 156–157. doi : 10.1109/ICCE-TW.2015.7216830 . ISBN 978-1-4799-8745-0 . S2CID 23295793 .
- ^ Фланаган 2002, с. 107
- ^ Jump up to: а б Льюис и Лофтус 2006, стр.454.
- ^ Оверби, Дж (2011). «Дифференциальная проверка предварительных условий: легкий, многоразовый анализ для инструментов рефакторинга». 2011 26-я Международная конференция IEEE/ACM по автоматизированной разработке программного обеспечения (ASE 2011) . стр. 303–312. дои : 10.1109/ASE.2011.6100067 . ISBN 978-1-4577-1639-3 . S2CID 5933208 .
- ^ Ли, К. (2014). «Остаточное расследование: прогнозируемое и точное обнаружение ошибок». Транзакции ACM по программной инженерии и методологии . 24 (2). дои : 10.1145/2656201 . S2CID 47112802 .
- ^ Мессенбёк, Ханспетер (25 марта 2002 г.). «Продвинутый C#: переопределение методов» (PDF) . Институт системного программного обеспечения, Университет Иоганна Кеплера в Линце, факультет компьютерных наук. стр. 6–8 . Проверено 2 августа 2011 г.
- ^ Малик 2006, с. 676
- ^ Мейер 2009, стр. 572-575.
- ^ Дейтель и Дейтель 2001, стр.474.
- ^
super().method
в Python 3 - см. https://docs.python.org/3/library/functions.html#super. Архивировано 26 октября 2018 г. на Wayback Machine.
См. также
[ редактировать ]- Наследование реализации
- Семантика наследования
- Перегрузка метода
- Полиморфизм в объектно-ориентированном программировании
- Шаблон метода шаблона
- Виртуальное наследование
- X-HTTP-Method-Override HTTP-заголовок
Ссылки
[ редактировать ]- Дейтель, Х.М. и Дейтель, П.Дж. (2001). Как программировать на Java (4-е изд.). Река Аппер-Седл, Нью-Джерси: Прентис-Холл.
- Льюис Дж. и Лофтус В. (2008). Java: программные решения (6-е изд.). Бостон, Массачусетс: Пирсон Аддисон Уэсли.
- Малик, Д.С. (2006). Программирование на C++: разработка программ, включая структуру данных. (3-е изд.). Вашингтон, округ Колумбия: Курс технологии.
- Фланаган, Дэвид. (2002). Коротко о Java. Получено с http://oreilly.com/catalog/9780596002831/preview#preview.
- Мейер, Бертран (2009). Прикосновение к классу: учимся хорошо программировать с объектами и контрактами . Спрингер.