Estou confuso sobre como substituir difere de ocultar em Java. Alguém pode fornecer mais detalhes sobre como eles diferem? Eu li o tutorial Java, mas o código de amostra ainda me deixou confuso.
Para ser mais claro, entendo bem a substituição. Meu problema é que não vejo como ocultar é diferente, exceto pelo fato de que um está no nível da instância enquanto o outro está no nível da classe.
Olhando para o código do tutorial Java:
public class Animal {
public static void testClassMethod() {
System.out.println("Class" + " method in Animal.");
}
public void testInstanceMethod() {
System.out.println("Instance " + " method in Animal.");
}
}
Então temos uma subclasse Cat
:
public class Cat extends Animal {
public static void testClassMethod() {
System.out.println("The class method" + " in Cat.");
}
public void testInstanceMethod() {
System.out.println("The instance method" + " in Cat.");
}
public static void main(String[] args) {
Cat myCat = new Cat();
Animal myAnimal = myCat;
Animal.testClassMethod();
myAnimal.testInstanceMethod();
}
}
Então eles dizem:
A saída deste programa é a seguinte:
Método de aula em Animal.
O método de instância em Cat.
Para mim, o fato de chamar um método de classe testClassMethod()
diretamente da Animal
classe executa o método em Animal
classe é bastante óbvio, nada de especial nisso . Em seguida, eles chamam testInstanceMethod()
de uma referência para myCat
, então, novamente, bastante óbvio que o método executado então é aquele na instância deCat
.
Pelo que vejo, a ocultação de chamadas se comporta como uma substituição, então por que fazer essa distinção? Se eu executar este código usando as classes acima:
Cat.testClassMethod();
Vou pegar:
O método de classe em Cat.
Mas se eu remover testClassMethod()
de Cat, então terei:
O método de classe em Animal.
O que me mostra que escrever um método estático, com a mesma assinatura do pai, em uma subclasse praticamente faz uma substituição.
Espero estar deixando claro onde estou confuso e alguém pode lançar alguma luz. Muito obrigado antecipadamente!
fonte
Respostas:
A substituição basicamente oferece suporte à vinculação tardia. Portanto, é decidido em tempo de execução qual método será chamado. É para métodos não estáticos.
Ocultar é para todos os outros membros (métodos estáticos, membros de instância, membros estáticos). Baseia-se na ligação inicial. Mais claramente, o método ou membro a ser chamado ou usado é decidido durante o tempo de compilação.
Em seu exemplo, a primeira chamada
Animal.testClassMethod()
é uma chamada para umstatic
método, portanto, é quase certo que método será chamado.Na segunda chamada,
myAnimal.testInstanceMethod()
você chama um método não estático. Isso é o que você chama de polimorfismo em tempo de execução. Não é decidido até o tempo de execução qual método deve ser chamado.Para maiores esclarecimentos, leia Overriding vs. Hiding .
fonte
private methods
? Eles não podem ser,overridden
pois a subclasse não sabe sobre sua existência. Portanto, eles podem serhidden
.Os métodos estáticos são ocultados, os métodos não estáticos são substituídos. A diferença é notável quando as chamadas não são qualificadas "something ()" vs "this.something ()".
Eu realmente não consigo colocar isso em palavras, então aqui vai um exemplo:
public class Animal { public static void something() { System.out.println("animal.something"); } public void eat() { System.out.println("animal.eat"); } public Animal() { // This will always call Animal.something(), since it can't be overriden, because it is static. something(); // This will call the eat() defined in overriding classes. eat(); } } public class Dog extends Animal { public static void something() { // This method merely hides Animal.something(), making it uncallable, but does not override it, or alter calls to it in any way. System.out.println("dog.something"); } public void eat() { // This method overrides eat(), and will affect calls to eat() System.out.println("dog.eat"); } public Dog() { super(); } public static void main(String[] args) { new Dog(); } }
RESULTADO:
fonte
husky.Animal();
vai imprimir animal.something or dog.something ? Acho que é ERRADO dizer ** que ** Isso sempre será chamado de Animal.something ().Animal()
, lembre-seAnimal()
é um construtor.something()
noAnimal()
sempre de chamada animal desomething()
é porque uma chamada para um método estático é resolvido em tempo de compilação ao invés de tempo de execução. Isso significa que a chamada do método estáticoAnimal()
está sempre chamando implicitamenteAnimal.something()
. Isso é bastante intuitivo se você pensar sobre isso: uma chamada para um método estático deve ser precedida por um nome de classe (ou sejaclassName.staticMethodName()
), a menos que a chamada seja da mesma classe.Esta é a diferença entre substituir e ocultar,
fonte
Se entendi sua pergunta corretamente, a resposta é "você já está substituindo".
"O que me mostra que escrever um método estático, com o mesmo nome do pai, em uma subclasse praticamente faz uma substituição."
Se você escrever um método em uma subclasse com exatamente o mesmo nome de um método em uma superclasse, ele substituirá o método da superclasse. A anotação @Override não é necessária para substituir um método. No entanto, torna seu código mais legível e força o compilador a verificar se você está realmente sobrescrevendo um método (e não digitou incorretamente o método da subclasse, por exemplo).
fonte
A substituição ocorre apenas com métodos de instância. Quando o tipo da variável de referência é Animal e o objeto é Cat, o método de instância é chamado de Cat (isso é sobreposto). Para o mesmo objeto acat, o método de classe Animal é usado.
public static void main(String[] args) { Animal acat = new Cat(); acat.testInstanceMethod(); acat.testClassMethod(); }
O resultado é:
fonte
public class First { public void Overriding(int i) { /* will be overridden in class Second */ } public static void Hiding(int i) { /* will be hidden in class Second because it's static */ } } public class Second extends First { public void Overriding(int i) { /* overridden here */ } public static void Hiding(int i) { /* hides method in class First because it's static */ } }
A regra para memorizar é simples: um método em uma classe extensível não pode mudar de estático para vazio e não pode mudar de vazio para estático. Isso causará um erro de compilação.
Mas se
void Name
for alterado paravoid Name
Substituir.E se
static Name
for alterado parastatic Name
Escondendo. (Tanto o método estático da subclasse quanto o da superclasse podem ser chamados, dependendo do tipo de referência usada para chamar o método.)fonte
Neste trecho de código, eu uso o modificador de acesso 'privado' em vez de 'estático' para mostrar a diferença entre ocultar métodos e substituir métodos.
class Animal { // Use 'static' or 'private' access modifiers to see how method hiding work. private void testInstancePrivateMethod(String source) { System.out.println("\tAnimal: instance Private method calling from "+source); } public void testInstanceMethodUsingPrivateMethodInside() { System.out.println("\tAnimal: instance Public method with using of Private method."); testInstancePrivateMethod( Animal.class.getSimpleName() ); } // Use default, 'protected' or 'public' access modifiers to see how method overriding work. protected void testInstanceProtectedMethod(String source) { System.out.println("\tAnimal: instance Protected method calling from "+source); } public void testInstanceMethodUsingProtectedMethodInside() { System.out.println("\tAnimal: instance Public method with using of Protected method."); testInstanceProtectedMethod( Animal.class.getSimpleName() ); } } public class Cat extends Animal { private void testInstancePrivateMethod(String source) { System.out.println("Cat: instance Private method calling from " + source ); } public void testInstanceMethodUsingPrivateMethodInside() { System.out.println("Cat: instance Public method with using of Private method."); testInstancePrivateMethod( Cat.class.getSimpleName()); System.out.println("Cat: and calling parent after:"); super.testInstanceMethodUsingPrivateMethodInside(); } protected void testInstanceProtectedMethod(String source) { System.out.println("Cat: instance Protected method calling from "+ source ); } public void testInstanceMethodUsingProtectedMethodInside() { System.out.println("Cat: instance Public method with using of Protected method."); testInstanceProtectedMethod(Cat.class.getSimpleName()); System.out.println("Cat: and calling parent after:"); super.testInstanceMethodUsingProtectedMethodInside(); } public static void main(String[] args) { Cat myCat = new Cat(); System.out.println("----- Method hiding -------"); myCat.testInstanceMethodUsingPrivateMethodInside(); System.out.println("\n----- Method overriding -------"); myCat.testInstanceMethodUsingProtectedMethodInside(); } }
Resultado:
fonte
Com base em meus estudos recentes de Java
Exemplo do livro OCP Java 7, páginas 70-71:
public class Point { private int xPos, yPos; public Point(int x, int y) { xPos = x; yPos = y; } public boolean equals(Point other){ .... sexy code here ...... } public static void main(String []args) { Point p1 = new Point(10, 20); Point p2 = new Point(50, 100); Point p3 = new Point(10, 20); System.out.println("p1 equals p2 is " + p1.equals(p2)); System.out.println("p1 equals p3 is " + p1.equals(p3)); //point's class equals method get invoked } }
mas se escrevermos o seguinte principal:
public static void main(String []args) { Object p1 = new Point(10, 20); Object p2 = new Point(50, 100); Object p3 = new Point(10, 20); System.out.println("p1 equals p2 is " + p1.equals(p2)); System.out.println("p1 equals p3 is " + p1.equals(p3)); //Object's class equals method get invoked }
No segundo principal, usamos a classe Object como tipo estático, então, quando chamamos o método equal no objeto Point, ele está esperando uma classe Point chegar como parâmetro, mas Object vindo. Portanto, a classe Object é igual ao método sendo executado, porque temos um equals (Object o) ali. Neste caso, a classe equals do Point não se sobrepõe, mas oculta o método equals da classe Object .
fonte
public class Parent { public static void show(){ System.out.println("Parent"); } } public class Child extends Parent{ public static void show(){ System.out.println("Child"); } } public class Main { public static void main(String[] args) { Parent parent=new Child(); parent.show(); // it will call parent show method } } // We can call static method by reference ( as shown above) or by using class name (Parent.show())
fonte
A página vinculada do tutorial Java explica o conceito de substituir e ocultar
A distinção entre ocultar um método estático e substituir um método de instância tem implicações importantes:
Voltando ao seu exemplo:
Animal myAnimal = myCat; /* invokes static method on Animal, expected. */ Animal.testClassMethod(); /* invokes child class instance method (non-static - it's overriding) */ myAnimal.testInstanceMethod();
A declaração acima não mostra esconder ainda.
Agora mude o código conforme abaixo para obter uma saída diferente:
Animal myAnimal = myCat; /* Even though myAnimal is Cat, Animal class method is invoked instead of Cat method*/ myAnimal.testClassMethod(); /* invokes child class instance method (non-static - it's overriding) */ myAnimal.testInstanceMethod();
fonte
Além dos exemplos listados acima, aqui está um pequeno código de amostra para esclarecer a distinção entre ocultar e substituir:
public class Parent { // to be hidden (static) public static String toBeHidden() { return "Parent"; } // to be overridden (non-static) public String toBeOverridden() { return "Parent"; } public void printParent() { System.out.println("to be hidden: " + toBeHidden()); System.out.println("to be overridden: " + toBeOverridden()); } } public class Child extends Parent { public static String toBeHidden() { return "Child"; } public String toBeOverridden() { return "Child"; } public void printChild() { System.out.println("to be hidden: " + toBeHidden()); System.out.println("to be overridden: " + toBeOverridden()); } } public class Main { public static void main(String[] args) { Child child = new Child(); child.printParent(); child.printChild(); } }
A chamada das
child.printParent()
saídas:a ser oculto: Pai
a ser substituído: filho
A chamada de
child.printChild()
outputs:to be hidden: Child
to be override: Child
Como podemos ver nas saídas acima (especialmente as saídas marcadas em negrito), a ocultação do método se comporta de maneira diferente da substituição.
Java permite ocultar e substituir apenas para métodos. A mesma regra não se aplica a variáveis. A substituição de variáveis não é permitida, portanto, as variáveis só podem ser ocultadas (nenhuma diferença entre variável estática ou não estática). O exemplo abaixo mostra como o método
getName()
é sobrescrito e a variávelname
é oculta:public class Main { public static void main(String[] args) { Parent p = new Child(); System.out.println(p.name); // prints Parent (since hiding) System.out.println(p.getName()); // prints Child (since overriding) } } class Parent { String name = "Parent"; String getName() { return name; } } class Child extends Parent { String name = "Child"; String getName() { return name; } }
fonte
Em tempo de execução, a versão filho de um método sobrescrito é sempre executada para uma instância, independentemente de a chamada do método ser definida em um método de classe pai ou filho. Dessa forma, o método pai nunca é usado, a menos que uma chamada explícita ao método pai seja referenciada, usando a sintaxe ParentClassName.method (). Como alternativa, em tempo de execução, a versão pai de um método oculto é sempre executada se a chamada para o método for definida na classe pai.
fonte
Na substituição de método , a resolução do método é feita pela JVM com base no objeto de tempo de execução. Enquanto na ocultação de método, a resolução do método é feita pelo compilador com base na referência. Portanto,
Se o código tivesse sido escrito como,
public static void main(String[] args) { Animal myCat = new Cat(); myCat.testClassMethod(); }
A saída seria a seguinte:
Método de classe em Animal.
fonte
É chamado de ocultar porque o compilador oculta a implementação do método da superclasse, quando a subclasse possui o mesmo método estático.
O compilador não tem visibilidade restrita para métodos sobrescritos e é apenas durante o tempo de execução que é decidido qual deles será usado.
fonte
Esta é a diferença entre substituir e ocultar:
Animal a = novo gato ();
a.testClassMethod () chamará o método na classe pai, pois é um exemplo de ocultação de método. O método a ser chamado é determinado pelo tipo da variável de referência e decidido em tempo de compilação.
a.testInstanceMethod () chamará o método na classe filha, pois é um exemplo de substituição de método. O método a ser chamado é determinado pelo objeto que é usado para chamar o método em tempo de execução.
fonte
Como o método estático oculto está acontecendo em java? A classe Cat está estendendo a classe Animal. Portanto, a classe Cat terá ambos os métodos estáticos (ou seja, o método estático da classe Child e o método estático da classe Parent). Mas como a JVM esconde o método estático Parent? Como está lidando com Heap e Stack?
fonte