Substituição de método - Method overriding
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 override
e virtual
ou 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 new
na 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.
super
Rectangle
Box
Box
Rectangle
Print
#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 .
Print
Box
Print
length
width
Box
As instruções a seguir irão instanciar objetos do tipo e , e chamar seus respectivos métodos:
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();
}
No C ++ 11 , semelhante ao Java, um método declarado final
na superclasse não pode ser substituído; além disso, um método pode ser declarado override
para 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 message
no exemplo abaixo, deve ser declarada explicitamente na inherit
clá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, ADVICE
o recurso message
recebe uma implementação diferente daquela de seu precursor na aula THOUGHT
.
Considere uma classe que usa instâncias para THOUGHT
e ADVICE
:
class
APPLICATION
create
make
feature
make
-- Run application.
do
(create {THOUGHT}).message;
(create {ADVICE}).message
end
end
Quando instanciada, a classe APPLICATION
produz 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 Thought
representa 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()
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."
A super
referê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).method
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
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
- Herança de implementação
- Semântica de herança
- Sobrecarga de método
- Polimorfismo na programação orientada a objetos
- Padrão de método de modelo
- Herança virtual
- Cabeçalho HTTP X-HTTP-Method-Override
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
- Substituição do método Java por Hemanth Balaji
- Introdução aos conceitos OOP e mais por Nirosh LwC
- Substituindo e ocultando métodos da Sun Microsystems