Estoy confundido sobre la diferencia entre anular y esconderse en Java. ¿Alguien puede proporcionar más detalles sobre cómo se diferencian? Leí el Tutorial de Java pero el código de muestra todavía me dejó confundido.
Para ser más claro, lo entiendo bien. Mi problema es que no veo cómo la ocultación es diferente, excepto por el hecho de que uno está en el nivel de instancia mientras que el otro está en el nivel de clase.
Mirando el código del tutorial de 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.");
}
}
Entonces tenemos una subclase 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();
}
}
Entonces dicen:
El resultado de este programa es el siguiente:
Método de clase en Animal.
El método de instancia en Cat.
Para mí, el hecho de que llamar a un método de clase testClassMethod()
directamente desde la Animal
clase ejecuta el método en Animal
clase es bastante obvio, no hay nada especial allí. Luego llaman a la testInstanceMethod()
de una referencia a myCat
, por lo que nuevamente es bastante obvio que el método ejecutado es el de la instancia de Cat
.
Por lo que veo, la ocultación de llamadas se comporta como anular, entonces, ¿por qué hacer esa distinción? Si ejecuto este código usando las clases anteriores:
Cat.testClassMethod();
Obtendré:
el método de clase en Cat.
Pero si elimino el testClassMethod()
de Cat, obtendré:
El método de clase en Animal.
Lo que me muestra que escribir un método estático, con la misma firma que en el padre, en una subclase prácticamente anula.
Ojalá esté dejando en claro dónde estoy confundido y alguien pueda arrojar algo de luz. ¡Muchas gracias de antemano!
fuente
Respuestas:
La anulación básicamente admite la vinculación tardía. Por lo tanto, se decide en tiempo de ejecución qué método se llamará. Es para métodos no estáticos.
La ocultación es para todos los demás miembros (métodos estáticos, miembros de instancia, miembros estáticos). Se basa en el enlace anticipado. Más claramente, el método o miembro que se llamará o utilizará se decide durante el tiempo de compilación.
En su ejemplo, la primera llamada
Animal.testClassMethod()
es una llamada a unstatic
método, por lo que es bastante seguro qué método se llamará.En la segunda llamada,
myAnimal.testInstanceMethod()
llamas a un método no estático. Esto es lo que llama polimorfismo en tiempo de ejecución. Hasta el momento de la ejecución, no se decide qué método se va a llamar.Para obtener más aclaraciones, lea Anular frente a ocultar .
fuente
private methods
? No pueden seroverridden
porque la subclase no sabe de su existencia. Por lo tanto, podrían serlohidden
.Los métodos estáticos están ocultos, los métodos no estáticos se anulan. La diferencia es notable cuando las llamadas no están calificadas como "algo ()" frente a "esto.algo ()".
Realmente parece que no puedo ponerlo en palabras, así que aquí va un ejemplo:
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(); } }
SALIDA:
fuente
husky.Animal();
will it print animal.something or dog.something ? Supongo que es INCORRECTO decir ** que ** Esto siempre llamará Animal.something ().Animal()
, recuerda queAnimal()
es un constructor.something()
en elAnimal()
siempre de llamada Animalsomething()
se debe a una llamada a un método estático se resuelve en tiempo de compilación en lugar de en tiempo de ejecución. Esto significa que la llamada al método estáticoAnimal()
siempre está llamando implícitamenteAnimal.something()
. Esto es bastante intuitivo si lo piensa: una llamada a un método estático debe estar precedida por un nombre de clase (es decirclassName.staticMethodName()
) a menos que la llamada esté en la misma clase.Esta es la diferencia entre anular y ocultar,
fuente
Si comprendo correctamente su pregunta, la respuesta es "ya está anulando".
"Lo que me muestra que escribir un método estático, con el mismo nombre que en el padre, en una subclase prácticamente anula".
Si escribe un método en una subclase con exactamente el mismo nombre que un método en una superclase, anulará el método de la superclase. No se requiere la anotación @Override para invalidar un método. Sin embargo, hace que su código sea más legible y obliga al compilador a verificar que en realidad está anulando un método (y que no escribió mal el método de la subclase, por ejemplo).
fuente
La anulación ocurre solo con los métodos de instancia. Cuando el tipo de la variable de referencia es Animal y el objeto es Cat, entonces se llama al método de instancia desde Cat (esto es invaluable). Para el mismo objeto acat, se utiliza el método de clase Animal.
public static void main(String[] args) { Animal acat = new Cat(); acat.testInstanceMethod(); acat.testClassMethod(); }
La salida es:
fuente
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 */ } }
La regla para memorizar es simple: un método en una clase que se extiende no puede cambiar de estático a vacío y no puede cambiar de vacío a estático. Causará un error de compilación.
Pero si
void Name
se cambia avoid Name
es Anulante.Y si
static Name
se cambia astatic Name
Es Oculto. (Se pueden llamar tanto al método estático de la subclase como al de la superclase, según el tipo de referencia utilizada para llamar al método).fuente
En este fragmento de código, uso el modificador de acceso 'privado' en lugar de 'estático' para mostrarle la diferencia entre los métodos de ocultación y los métodos de anulación.
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(); } }
Salida:
fuente
Basado en mis estudios recientes de Java
Ejemplo del libro OCP Java 7, página 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 } }
pero si escribimos el siguiente main:
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 }
En la segunda principal, usamos la clase Object como tipo estático, por lo que cuando llamamos al método equal en el objeto Point, está esperando que llegue una clase Point como parámetro, pero viene Object. Entonces, la clase Object es igual al método que se está ejecutando, porque tenemos un igual (Object o) allí. En este caso, la clase de Point es igual a dosen't anula, pero oculta la clase de objeto es igual al método .
fuente
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())
fuente
La página del tutorial de Java vinculada explica el concepto de anular y ocultar
La distinción entre ocultar un método estático y anular un método de instancia tiene implicaciones importantes:
Volviendo a tu ejemplo:
Animal myAnimal = myCat; /* invokes static method on Animal, expected. */ Animal.testClassMethod(); /* invokes child class instance method (non-static - it's overriding) */ myAnimal.testInstanceMethod();
La declaración anterior aún no muestra la ocultación.
Ahora cambie el código de la siguiente manera para obtener una salida 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();
fuente
Además de los ejemplos enumerados anteriormente, aquí hay un pequeño código de muestra para aclarar la distinción entre ocultar y anular:
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(); } }
La llamada de
child.printParent()
salidas:para estar oculto: Padre
para ser anulado: hijo
La llamada de las
child.printChild()
salidas:estar oculto: Niño
para ser anulado: niño
Como podemos ver en las salidas anteriores (especialmente las salidas marcadas en negrita), la ocultación de métodos se comporta de manera diferente a la anulación.
Java permite ocultar y anular solo los métodos. La misma regla no se aplica a las variables. No se permite anular las variables, por lo que las variables solo se pueden ocultar (no hay diferencia entre variable estática o no estática). El siguiente ejemplo muestra cómo
getName()
se anula el método yname
se oculta la variable :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; } }
fuente
En tiempo de ejecución, la versión secundaria de un método anulado siempre se ejecuta para una instancia, independientemente de si la llamada al método está definida en un método de clase padre o hijo. De esta manera, el método principal nunca se usa a menos que se haga referencia a una llamada explícita al método principal, usando la sintaxis ParentClassName.method (). Alternativamente, en tiempo de ejecución, la versión principal de un método oculto siempre se ejecuta si la llamada al método está definida en la clase principal.
fuente
En método de anulación , la JVM realiza la resolución del método en función del objeto de tiempo de ejecución. Mientras que en la ocultación de métodos, la resolución del método la realiza el compilador sobre la base de la referencia. Así,
Si el código se hubiera escrito como,
public static void main(String[] args) { Animal myCat = new Cat(); myCat.testClassMethod(); }
La salida sería la siguiente:
Método de clase en Animal.
fuente
Se llama ocultación porque el compilador oculta la implementación del método de superclase, cuando la subclase tiene el mismo método estático.
El compilador no tiene visibilidad restringida para los métodos anulados y solo durante el tiempo de ejecución se decide cuál se usa.
fuente
Esta es la diferencia entre anular y ocultar:
Animal a = gato nuevo ();
a.testClassMethod () llamará al método en la clase principal ya que es un ejemplo de ocultación de métodos. El método que se llamará está determinado por el tipo de variable de referencia y se decide en el momento de la compilación.
a.testInstanceMethod () llamará al método en la clase secundaria ya que es un ejemplo de anulación de método. El método que se va a llamar está determinado por el objeto que se utiliza para llamar al método en tiempo de ejecución.
fuente
¿Cómo ocurre la ocultación del método estático en Java? La clase de gatos se está extendiendo a la clase de animales. Entonces, en la clase Cat tendrá ambos métodos estáticos (me refiero al método estático de la clase Child y al método estático de la clase Parent) ¿Pero cómo JVM oculta el método estático Parent? ¿Cómo se maneja en Heap and Stack?
fuente