Переопределение метода - Method overriding

Иллюстрация

Переопределение метода в объектно-ориентированном программировании - это языковая функция, которая позволяет подклассу или дочернему классу предоставлять конкретную реализацию метода, который уже предоставлен одним из его суперклассов или родительских классов. Он допускает определенный тип полиморфизма ( подтипы ). Реализация в подклассе переопределяет (заменяет) реализацию в суперклассе, предоставляя метод с тем же именем, такими же параметрами или сигнатурой и тем же возвращаемым типом, что и метод в родительском классе. Версия выполняемого метода будет определяться объектом, который используется для его вызова. Если для вызова метода используется объект родительского класса, то будет выполнена версия в родительском классе, но если для вызова метода используется объект подкласса, то будет выполнена версия в дочернем классе. Некоторые языки позволяют программисту предотвратить переопределение метода.

Примеры для конкретных языков

Ада

Ада по умолчанию обеспечивает переопределение метода. Чтобы способствовать раннему обнаружению ошибок (например, орфографической ошибки), можно указать, когда метод, как ожидается, будет фактически заменять или нет. Это будет проверено компилятором.

  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 #

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перед ним.

В приведенном выше примере скрытие вызывает следующее:

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 ++

В C ++ нет ключевого слова, которое подкласс может использовать в Java для вызова версии суперкласса метода, который он хочет переопределить. Вместо этого используется имя родительского или базового класса, за которым следует оператор разрешения области видимости . Например, следующий код представляет два класса : базовый класс и производный класс . переопределяет метод класса , чтобы также напечатать его высоту. superRectangleBoxBoxRectanglePrint

#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_;
}

Метод в классе , вызывая родительскую версию метода , также может выводить частные переменные и базовый класс. В противном случае эти переменные недоступны для . PrintBoxPrint lengthwidthBox

Следующие операторы будут создавать экземпляры объектов типа и и вызывать их соответствующие методы: RectangleBoxPrint

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

В Delphi переопределение метода выполняется с помощью переопределения директивы , но только в том случае, если метод был отмечен динамическими или виртуальными директивами.

Унаследовали зарезервированное слово должно быть названо , когда вы хотите назвать супер-класс поведения

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. Переопределение - это одна из трех форм адаптации функций, классифицируемых как переопределение . Переопределение также охватывает осуществления , в котором реализация предусмотрена функция , которая была отсроченной (реферат) в родительском классе, и undefinition , в котором особенность , которая была эффективной (бетон) в родительском становится отложено снова в классе наследника. Когда функция переопределяется, имя функции сохраняется за классом-наследником, но свойства функции, такие как подпись, контракт (с учетом ограничений для предусловий и постусловий ) и / или реализация, будут отличаться у наследника. Если исходный объект в родительском классе, называемый предшественником объекта-наследника , действует, то будет действовать переопределенный объект в наследнике. Если предшественник отложен, функция наследника будет отложена.

Намерение переопределить функцию, как 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.

В переопределенной функции доступ к предшественнику функции можно получить с помощью ключевого слова language 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 , когда подкласс содержит метод, который переопределяет метод суперкласса, он также может вызвать метод суперкласса с помощью ключевого слова super . Пример:

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()AdviceThoughtAdvicemessage()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Ссылка может быть

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, стал суперклассом.

Котлин

В 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

В Python , когда подкласс содержит метод, который переопределяет метод суперкласса, вы также можете вызвать метод суперкласса, вызвав вместо . Пример: super(Subclass, self).methodself.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

Примечания

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

использованная литература

  • Дейтель, Х. М. и Дейтель, П. Дж. (2001). Java, как программировать (4-е изд.). Река Аппер Сэдл, штат Нью-Джерси: Prentice Hall.
  • Льюис, Дж. И Лофтус, В. (2008). Java: программные решения (6-е изд.). Бостон, Массачусетс: Пирсон Аддисон Уэсли.
  • Малик, Д.С. (2006). Программирование на C ++: проектирование программ, включая структуру данных. (3-е изд.). Вашингтон, округ Колумбия: Технология курса.
  • Фланаган, Дэвид. (2002). Java в двух словах. Получено с http://oreilly.com/catalog/9780596002831/preview#preview.
  • Мейер, Бертран (2009). Прикосновение класса: научиться хорошо программировать с объектами и контрактами . Springer.

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