Цель модификатора повторного ввода - предотвратить распространенную логическую ошибку.
Я предполагаю, что общеизвестно, как повторное введение ключевого слова исправляет предупреждение и объяснит, почему генерируется предупреждение и почему ключевое слово включено в язык. Рассмотрим приведенный ниже код delphi;
TParent = Class
Public
Procedure Procedure1(I : Integer); Virtual;
Procedure Procedure2(I : Integer);
Procedure Procedure3(I : Integer); Virtual;
End;
TChild = Class(TParent)
Public
Procedure Procedure1(I : Integer);
Procedure Procedure2(I : Integer);
Procedure Procedure3(I : Integer); Override;
Procedure Setup(I : Integer);
End;
procedure TParent.Procedure1(I: Integer);
begin
WriteLn('TParent.Procedure1');
end;
procedure TParent.Procedure2(I: Integer);
begin
WriteLn('TParent.Procedure2');
end;
procedure TChild.Procedure1(I: Integer);
begin
WriteLn('TChild.Procedure1');
end;
procedure TChild.Procedure2(I: Integer);
begin
WriteLn('TChild.Procedure2');
end;
procedure TChild.Setup(I : Integer);
begin
WriteLn('TChild.Setup');
end;
Procedure Test;
Var
Child : TChild;
Parent : TParent;
Begin
Child := TChild.Create;
Child.Procedure1(1); // outputs TChild.Procedure1
Child.Procedure2(1); // outputs TChild.Procedure2
Parent := Child;
Parent.Procedure1(1); // outputs TParent.Procedure1
Parent.Procedure2(1); // outputs TParent.Procedure2
End;
Учитывая приведенный выше код, обе процедуры в TParent скрыты. Сказать, что они скрыты, означает, что процедуры не могут быть вызваны через указатель TChild. Компиляция примера кода выдает одно предупреждение;
[Предупреждение DCC] Project9.dpr (19): Метод W1010 «Процедура1» скрывает виртуальный метод базового типа «TParent»
Почему только предупреждение для виртуальной функции, а не для другой? Оба скрыты.
Достоинство Delphi заключается в том, что разработчики библиотек могут выпускать новые версии, не опасаясь нарушить логику существующего клиентского кода. Это отличается от Java, где добавление новых функций к родительскому классу в библиотеке сопряжено с опасностью, поскольку классы неявно являются виртуальными. Допустим, что TParent свыше живет в сторонней библиотеке, а изготовитель библиотеки выпускает новую версию ниже.
// version 2.0
TParent = Class
Public
Procedure Procedure1(I : Integer); Virtual;
Procedure Procedure2(I : Integer);
Procedure Procedure3(I : Integer); Virtual;
Procedure Setup(I : Integer); Virtual;
End;
procedure TParent.Setup(I: Integer);
begin
// important code
end;
Представьте, что в нашем клиентском коде был следующий код
Procedure TestClient;
Var
Child : TChild;
Begin
Child := TChild.Create;
Child.Setup;
End;
Для клиента не имеет значения, скомпилирован ли код с версией 2 или 1 библиотеки, в обоих случаях TChild.Setup вызывается по назначению пользователя. И в библиотеке;
// library version 2.0
Procedure TestLibrary(Parent : TParent);
Begin
Parent.Setup;
End;
Если TestLibrary вызывается с параметром TChild, все работает как задумано. Разработчик библиотеки не знает о TChild.Setup, и в Delphi это не причиняет им никакого вреда. Вызов выше правильно разрешает TParent.Setup.
Что произойдет в эквивалентной ситуации в Java? TestClient будет работать правильно, как задумано. TestLibrary не будет. В Java все функции предполагаются виртуальными. Parent.Setup преобразуется в TChild.Setup, но помните, что когда был написан TChild.Setup, они не знали о будущем TParent.Setup, поэтому они, конечно, никогда не будут вызывать наследуемый. Поэтому, если разработчик библиотеки намеревался вызвать TParent.Setup, его не будет, независимо от того, что они делают. И, конечно, это может быть катастрофическим.
Таким образом, объектная модель в Delphi требует явного объявления виртуальных функций в цепочке дочерних классов. Побочным эффектом этого является то, что легко забыть добавить модификатор override для дочерних методов. Наличие ключевого слова Reintroduce удобно для программиста. Delphi был спроектирован таким образом, чтобы при генерации предупреждения программиста аккуратно убедили явно заявить о своих намерениях в таких ситуациях.