Anular vs Ocultar Java - Confundido


88

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 Animalclase ejecuta el método en Animalclase 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!


Respuestas:


103

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 un staticmé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 .


3
¡Gracias por la respuesta rápida, esto lo aclara! Noté que en el ejemplo de JavaRanch, usaron la variable para llamar al método de la clase en lugar de usar la clase directamente, lo que lo hace más fácil de entender. Supongo que en el tutorial de Java usaron la clase directamente porque usar una instancia para llamar a un método estático probablemente no sea una buena práctica, pero deberían haber usado myAnimal.testClassMethod () en lugar de Animal.testClassMethod () .
Lostlinkpr

+1 por ser capaz de expresarlo correctamente con palabras, en lugar de hacerlo con un ejemplo. :)
WhyNotHugo

@Kazekage Gaara ¿Hay diferencia entre sobrecargar y esconderse?
gstackoverflow

1
Estoy de acuerdo, por supuesto, con la respuesta, pero ¿qué tal private methods? No pueden ser overriddenporque la subclase no sabe de su existencia. Por lo tanto, podrían serlo hidden.
Paschalis

¡Excelente respuesta! Aunque podría agregar el ejemplo en coderanch para completar :)
Shubham Mittal

19

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:

animal.something
dog.eat

1
ok, entonces ¿qué pasaría si llamo `dog husky = new dog (); ' y llamar husky.Animal();will it print animal.something or dog.something ? Supongo que es INCORRECTO decir ** que ** Esto siempre llamará Animal.something ()
amarnath harish

@amarnathharish No puedes hacer .Animal(), recuerda que Animal()es un constructor.
Dude156

Y como mayor aclaración a cualquiera que se pregunte, la razón por la que something()en el Animal() siempre de llamada Animal something()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ático Animal()siempre está llamando implícitamente Animal.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 decir className.staticMethodName()) a menos que la llamada esté en la misma clase.
Dude156

14

Esta es la diferencia entre anular y ocultar,

  1. Si tanto el método en la clase principal como en la clase secundaria son un método de instancia, se llama anulaciones.
  2. Si tanto el método en la clase principal como en la clase secundaria son métodos estáticos, se llama a ocultar.
  3. Un método no puede ser estático en el padre y como instancia en el hijo. y viceversa.

ingrese la descripción de la imagen aquí


3
Cortó y pegó esa tabla directamente del tutorial que el OP dijo que no lo ayudó a entender.
wolfcastle

La tabla lo deja muy claro, en los ejemplos no se consideraron todos los casos.
tutak

3

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


1
Esta respuesta no aborda la instancia frente a los métodos estáticos con respecto a anular / ocultar.
Paul Bellora

3

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:

The instance method in Cat.
Class method in Animal.

2
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 Namese cambia a void Namees Anulante.

Y si static Namese cambia a static NameEs 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).


1

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:

----- Method hiding -------
Cat: instance Public method with using of Private method.
Cat: instance Private method calling from Cat
Cat: and calling parent after:
   Animal: instance Public method with using of Private method.
   Animal: instance Private method calling from Animal

----- Method overriding -------
Cat: instance Public method with using of Protected method.
Cat: instance Protected method calling from Cat
Cat: and calling parent after:
   Animal: instance Public method with using of Protected method.
Cat: instance Protected method calling from Animal

Me pregunto por qué no obtuvo los votos a favor ... respuesta muy apreciada.
amarnath harish

0

Basado en mis estudios recientes de Java

  • anulación del método , cuando la subclase tiene el mismo método con la misma firma en la subclase.
  • Método de ocultación , cuando la subclase tienen el mismo nombre del método, pero parámetro diferente. En este caso, no anula el método principal, sino que lo oculta.

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 .


0
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())

0

La página del tutorial de Java vinculada explica el concepto de anular y ocultar

Un método de instancia en una subclase con la misma firma (nombre, más el número y el tipo de sus parámetros) y el tipo de retorno como método de instancia en la superclase anula el método de la superclase.

Si una subclase define un método estático con la misma firma que un método estático en la superclase, entonces el método de la subclase oculta el de la superclase.

La distinción entre ocultar un método estático y anular un método de instancia tiene implicaciones importantes:

  1. La versión del método de instancia anulado que se invoca es la de la subclase.
  2. La versión del método estático oculto que se invoca depende de si se invoca desde la superclase o desde la subclase.

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();

Estoy tratando de entender qué significa la palabra ocultarse. ¿Qué esconde qué? ¿Está oculto el método estático en la clase Parent porque (menos esperado) se invoca? ¿O el método estático en la clase Child está oculto porque no se invoca?
escorpión

0

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 y namese 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;
    }
}

0

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.


0

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.


0

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.


0

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.


-1

¿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?


Esta no es una respuesta. Es una extensión de la pregunta formulada. Puede haber sido una pregunta separada en sí misma o una parte de los comentarios sobre la pregunta.
Sri9911
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.