Substituição de método - Method overriding

Ilustração

A substituição de método , na programação orientada a objetos , é um recurso de linguagem que permite a uma subclasse ou classe filha fornecer uma implementação específica de um método que já é fornecido por uma de suas superclasses ou classes pai. Ele permite um tipo específico de polimorfismo ( subtipagem ). A implementação na subclasse substitui (substitui) a implementação na superclasse, fornecendo um método que tem o mesmo nome, os mesmos parâmetros ou assinatura e o mesmo tipo de retorno que o método na classe pai. A versão de um método executado será determinada pelo objeto usado para invocá-lo. Se um objeto de uma classe pai for usado para invocar o método, a versão na classe pai será executada, mas se um objeto da subclasse for usado para invocar o método, a versão na classe filha será executada. Algumas linguagens permitem que um programador impeça que um método seja sobrescrito.

Exemplos específicos de linguagem

Ada

Ada fornece substituição de método por padrão. Para favorecer a detecção precoce de erros (por exemplo, um erro ortográfico), é possível especificar quando se espera que um método seja realmente substituído ou não. Isso será verificado pelo compilador.

  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 # oferece suporte à substituição de método, mas apenas se explicitamente solicitado usando os modificadores overridee virtualou 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().
}

Ao substituir um método por outro, as assinaturas dos dois métodos devem ser idênticas (e com a mesma visibilidade). Em C #, métodos de classe , indexadores , propriedades e eventos podem ser substituídos.

Os métodos não virtuais ou estáticos não podem ser substituídos. O método de base substituído deve ser virtual , abstrato ou substituir .

Além dos modificadores que são usados ​​para substituir métodos, C # permite ocultar uma propriedade ou método herdado. Isso é feito usando a mesma assinatura de uma propriedade ou método, mas adicionando o modificador newna frente dela.

No exemplo acima, ocultar causa o seguinte:

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 ++ não tem a palavra-chave que uma subclasse pode usar em Java para invocar uma versão da superclasse de um método que deseja substituir. Em vez disso, o nome da classe pai ou base é usado seguido pelo operador de resolução de escopo . Por exemplo, o código a seguir apresenta duas classes , a classe base e a classe derivada . sobrescreve o método da classe , para também imprimir sua altura. 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_;
}

O método em classe , ao invocar a versão pai do método , também é capaz de produzir as variáveis privadas e da classe base. Caso contrário, essas variáveis ​​ficam inacessíveis para . PrintBoxPrint lengthwidthBox

As instruções a seguir irão instanciar objetos do tipo e , e chamar seus respectivos métodos: 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();
}

No C ++ 11 , semelhante ao Java, um método declarado finalna superclasse não pode ser substituído; além disso, um método pode ser declarado overridepara fazer o compilador verificar se ele substitui um método na classe base.

Delphi

No Delphi , a substituição do método é feita com a substituição da diretiva , mas apenas se um método foi marcado com as diretivas dinâmicas ou virtuais .

A palavra reservada herdada deve ser chamada quando você deseja chamar o comportamento da superclasse

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

Em Eiffel , a redefinição de recursos é análoga à substituição de métodos em C ++ e Java. A redefinição é uma das três formas de adaptação de recursos classificadas como redeclaração . A redeclaração também cobre a efetivação , na qual uma implementação é fornecida para um recurso que foi adiado (abstrato) na classe pai, e a indefinição , na qual um recurso que era efetivo (concreto) no pai é adiado novamente na classe herdeira. Quando um recurso é redefinido, o nome do recurso é mantido pela classe herdeira, mas as propriedades do recurso, como sua assinatura, contrato (respeitando as restrições de pré - condições e pós - condições ) e / ou implementação serão diferentes no herdeiro. Se o recurso original na classe pai, chamado de precursor do recurso herdeiro , for eficaz, o recurso redefinido na classe herdeira será eficaz. Se o precursor for adiado, a característica no herdeiro será adiada.

A intenção de redefinir um recurso, como messageno exemplo abaixo, deve ser declarada explicitamente na inheritcláusula da classe herdeira.

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

Na aula, ADVICEo recurso messagerecebe uma implementação diferente daquela de seu precursor na aula THOUGHT.

Considere uma classe que usa instâncias para THOUGHTe ADVICE:

class
    APPLICATION
create
    make
feature 
    make
            -- Run application.
        do
            (create {THOUGHT}).message;
            (create {ADVICE}).message
        end
end

Quando instanciada, a classe APPLICATIONproduz a seguinte saída:

I feel like I am diagonally parked in a parallel universe.
Warning: Dates in calendar are closer than they appear.

Dentro de um recurso redefinido, o acesso ao precursor do recurso pode ser obtido usando a palavra-chave language Precursor. Suponha que a implementação de seja alterada da seguinte forma: {ADVICE}.message

    message
            -- Precursor
        do
            print ("Warning: Dates in calendar are closer than they appear.%N")
            Precursor
        end

A invocação do recurso agora inclui a execução de e produz a seguinte saída: {THOUGHT}.message

Warning: Dates in calendar are closer than they appear.
I feel like I am diagonally parked in a parallel universe.

Java

Em Java , quando uma subclasse contém um método que substitui um método da superclasse, ela também pode invocar o método da superclasse usando a palavra - chavesuper . Exemplo:

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.");
    }
}

Classe Thoughtrepresenta a superclasse e implementa uma chamada de método . A subclasse chamada herda todos os métodos que podem estar na classe. No entanto, a classe substitui o método , substituindo sua funcionalidade de . 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."

A superreferência pode ser

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.
      }

Existem métodos que uma subclasse não pode substituir. Por exemplo, em Java, um método declarado final na superclasse não pode ser substituído. Os métodos declarados privados ou estáticos também não podem ser substituídos porque são implicitamente finais. Também é impossível que uma classe declarada final se torne uma superclasse.

Kotlin

No Kotlin , podemos simplesmente substituir uma função como esta (observe que a função deve ser 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")
}

Pitão

Em Python , quando uma subclasse contém um método que substitui um método da superclasse, você também pode chamar o método da superclasse chamando em vez de . Exemplo: 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

Rubi

Em Ruby, quando uma subclasse contém um método que sobrescreve um método da superclasse, você também pode chamar o método da superclasse chamando super nesse método sobrescrito. Você pode usar o alias se quiser manter o método sobrescrito disponível fora do método sobrescrito como mostrado com 'super_message' abaixo.

Exemplo:

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

Notas

Veja também

Referências

  • Deitel, H. M & Deitel, PJ (2001). Java How to Program (4ª ed.). Upper Saddle River, NJ: Prentice Hall.
  • Lewis, J. & Loftus, W. (2008). Java: Soluções de software (6ª ed.). Boston, MA: Pearson Addison Wesley.
  • Malik, DS (2006). Programação C ++: Design de programa incluindo estrutura de dados. (3ª ed.). Washington, DC: Curso de Tecnologia.
  • Flanagan, David. (2002). Java em poucas palavras. Obtido em http://oreilly.com/catalog/9780596002831/preview#preview
  • Meyer, Bertrand (2009). Toque de classe: aprendendo a programar bem com objetos e contratos . Springer.

links externos