Override vs nascondere Java - Confuso


88

Sono confuso su come l'override differisca dal nascondersi in Java. Qualcuno può fornire maggiori dettagli su come questi differiscono? Ho letto il tutorial Java ma il codice di esempio mi ha lasciato ancora confuso.

Per essere più chiari, capisco bene l'override. Il mio problema è che non vedo come l'occultamento sia diverso, tranne per il fatto che uno è a livello di istanza mentre l'altro è a livello di classe.

Guardando il codice del 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.");
    }
}

Quindi abbiamo una sottoclasse 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();
    }
}

Poi dicono:

L'output di questo programma è il seguente:

Metodo di classe in Animal.

Il metodo di istanza in Cat.

Per me, il fatto che chiamando un metodo di classe testClassMethod()direttamente dalla Animalclasse esegua il metodo in Animalclasse è abbastanza ovvio, niente di speciale lì. Quindi chiamano testInstanceMethod()da un riferimento a myCat, quindi ancora abbastanza ovvio che il metodo eseguito in quel momento è quello nell'istanza di Cat.

Da quello che vedo, la chiamata nascosta si comporta proprio come l'override, quindi perché fare questa distinzione? Se eseguo questo codice utilizzando le classi sopra:

Cat.testClassMethod();

Otterrò: il metodo di classe in Cat. Ma se rimuovo testClassMethod()da Cat, ottengo: Il metodo di classe in Animal.

Il che mi mostra che scrivere un metodo statico, con la stessa firma del genitore, in una sottoclasse fa praticamente un override.

Spero di chiarire dove sono confuso e che qualcuno possa fare luce. Grazie mille in anticipo!


Risposte:


103

L'override supporta fondamentalmente l'associazione tardiva. Pertanto, in fase di esecuzione viene deciso quale metodo verrà chiamato. È per metodi non statici.

Nascondere è per tutti gli altri membri (metodi statici, membri di istanza, membri statici). Si basa sull'associazione anticipata. Più chiaramente, il metodo o il membro da chiamare o utilizzare viene deciso durante la fase di compilazione.

Nel tuo esempio, la prima chiamata Animal.testClassMethod()è una chiamata a un staticmetodo, quindi è abbastanza sicuro quale metodo verrà chiamato.

Nella seconda chiamata, myAnimal.testInstanceMethod()chiami un metodo non statico. Questo è ciò che chiamate polimorfismo di runtime. Non si decide fino al runtime quale metodo deve essere chiamato.

Per ulteriori chiarimenti, leggi Overriding Vs Hiding .


3
Grazie per la rapida risposta, questo lo chiarisce! Ho notato che nell'esempio JavaRanch, hanno usato la variabile per chiamare il metodo della classe invece di usare direttamente la classe, il che lo rende più facile da capire. Immagino che nel tutorial Java abbiano usato la classe direttamente perché usare un'istanza per chiamare un metodo statico probabilmente non è una buona pratica, ma avrebbero dovuto usare myAnimal.testClassMethod () invece di Animal.testClassMethod () .
Lostlinkpr

+1 per essere in grado di esprimerlo a parole correttamente, piuttosto che con l'esempio! :)
WhyNotHugo

@ Kazekage Gaara C'è differenza tra sovraccaricare e nascondersi?
gstackoverflow

1
Sono d'accordo ovviamente con la risposta, ma che ne dici private methods? Non possono essere overriddenpoiché la sottoclasse non sa della loro esistenza .. Quindi potrebbero esserlo hidden.
Paschalis

Ottima risposta! Anche se potresti aggiungere l'esempio al coderanch per completezza :)
Shubham Mittal

19

I metodi statici sono nascosti, i metodi non statici vengono sovrascritti. La differenza è notevole quando le chiamate non sono qualificate "something ()" rispetto a "this.something ()".

Non riesco davvero a spiegarlo a parole, quindi ecco un esempio:

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

}

PRODUZIONE:

animal.something
dog.eat

1
ok, allora cosa succederebbe se chiamassi "dog husky = new dog ();" e chiamerà husky.Animal();stamperà animal.something o dog.something ? Immagino sia SBAGLIATO dire ** che ** Questo chiamerà sempre Animal.something ()
amarnath harish

@amarnathharish Non puoi farlo .Animal(), ricorda Animal()è un costruttore.
Amico156

E come ulteriore chiarimento per chiunque chiedendo, il motivo per il something()nel Animal() sempre del Richiamo di something()è perché una chiamata a un metodo statico viene risolto in fase di compilazione, piuttosto che in fase di esecuzione. Ciò significa che la chiamata al metodo statico chiama Animal()sempre in modo implicito Animal.something(). Questo è abbastanza intuitivo se ci pensate: una chiamata a un metodo statico deve essere preceduta dal nome di una classe (cioè className.staticMethodName()) a meno che la chiamata non sia nella stessa classe.
Amico156

14

Questa è la differenza tra override e nascondere,

  1. Se entrambi i metodi nella classe genitore e nella classe figlia sono un metodo di istanza, vengono chiamati sostituzioni.
  2. Se entrambi i metodi nella classe genitore e nella classe figlia sono metodi statici, viene chiamato hiding.
  3. Un metodo non può essere statico nel genitore e come istanza nel figlio. e viceversa.

inserisci qui la descrizione dell'immagine


3
Hai tagliato e incollato quella tabella direttamente dal tutorial che l'OP ha detto che non lo ha aiutato a capire.
wolfcastle

La tabella rende molto chiaro, negli esempi non tutti i casi sono stati considerati.
tutak

3

Se capisco correttamente la tua domanda, la risposta è "stai già ignorando".

"Il che mi mostra che scrivere un metodo statico, con lo stesso nome del genitore, in una sottoclasse fa praticamente un override."

Se scrivi un metodo in una sottoclasse con esattamente lo stesso nome di un metodo in una superclasse, sovrascriverà il metodo della superclasse. L'annotazione @Override non è necessaria per sovrascrivere un metodo. Tuttavia, rende il tuo codice più leggibile e forza il compilatore a verificare che tu stia effettivamente sovrascrivendo un metodo (e non ha scritto male il metodo della sottoclasse, ad esempio).


1
Questa risposta non riesce ad affrontare i metodi istanza vs statici per quanto riguarda l'override / occultamento.
Paul Bellora

3

L'override avviene solo con i metodi di istanza. Quando il tipo della variabile di riferimento è Animal e l'oggetto è Cat, il metodo di istanza viene chiamato da Cat (si tratta di override). Per lo stesso oggetto acat viene utilizzato il metodo di classe di Animal.

public static void main(String[] args) {
    Animal acat = new Cat();
    acat.testInstanceMethod();
    acat.testClassMethod();

}

L'output è:

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 regola per memorizzare è semplice: un metodo in una classe extending non può cambiare static in void e non può cambiare void in static. Causerà un errore di compilazione.

Ma se void Nameviene modificato in void NameOverriding.

E se static Nameè cambiato in static Namesi nasconde. (È possibile chiamare sia il metodo statico della sottoclasse che quello della superclasse, a seconda del tipo di riferimento utilizzato per chiamare il metodo.)


1

In questo frammento di codice utilizzo il modificatore di accesso "privato" invece di "statico" per mostrare la differenza tra metodi nascosti e metodi di sostituzione.

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

Produzione:

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

mi chiedo perché non ha ricevuto i voti positivi .. risposta molto apprezzata.
amarnath harish

0

Sulla base dei miei recenti studi su Java

  • sovrascrittura del metodo , quando la sottoclasse ha lo stesso metodo con la stessa firma nella sottoclasse.
  • Metodo nascosto , quando la sottoclasse ha lo stesso nome di metodo, ma parametro diverso. In questo caso, non stai sovrascrivendo il metodo genitore, ma nascondendolo.

Esempio dal libro OCP Java 7, pagina 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
  }
}

ma se scriviamo il seguente 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
  }

Nel secondo main, usiamo la classe Object come tipo statico, quindi quando chiamiamo il metodo equal nell'oggetto Point, è in attesa che una classe Point arrivi come parametro, ma Object in arrivo. Quindi la classe Object è uguale al metodo che viene eseguito, perché abbiamo un uguale (Object o) lì. In questo caso, la classe Point equals non sovrascrive, ma nasconde il metodo Object class equals .


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 pagina del tutorial java collegata spiega il concetto di sovrascrittura e occultamento

Un metodo di istanza in una sottoclasse con la stessa firma (nome, più il numero e il tipo dei suoi parametri) e il tipo restituito come metodo di istanza nella superclasse sostituisce il metodo della superclasse.

Se una sottoclasse definisce un metodo statico con la stessa firma di un metodo statico nella superclasse, il metodo nella sottoclasse nasconde quello nella superclasse.

La distinzione tra nascondere un metodo statico e sovrascrivere un metodo di istanza ha importanti implicazioni:

  1. La versione del metodo di istanza sovrascritto che viene richiamato è quella nella sottoclasse.
  2. La versione del metodo statico nascosto che viene richiamato dipende dal fatto che venga richiamato dalla superclasse o dalla sottoclasse.

Tornando al tuo esempio:

Animal myAnimal = myCat;

 /* invokes static method on Animal, expected. */
 Animal.testClassMethod(); 

 /* invokes child class instance method (non-static - it's overriding) */
 myAnimal.testInstanceMethod();

La dichiarazione di cui sopra non mostra ancora l'occultamento.

Ora cambia il codice come di seguito per ottenere un output diverso:

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

Sto cercando di capire cosa significhi la parola nascondersi. Cosa nasconde cosa? Il metodo statico nella classe Parent è nascosto perché (meno previsto) viene richiamato? O il metodo statico nella classe Child è nascosto, perché non viene richiamato?
scorpione

0

Oltre agli esempi elencati sopra, ecco un piccolo codice di esempio per chiarire la distinzione tra nascondere e sovrascrivere:

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 chiamata delle child.printParent()uscite:
da nascondere: Genitore
da sovrascrivere: Figlio

Il richiamo delle child.printChild()uscite:
da nascondere: figlio
da sovrascrivere: figlio

Come si può vedere dagli output sopra (specialmente gli output contrassegnati in grassetto), il metodo che nasconde si comporta in modo diverso dall'override.

Java consente sia di nascondere che di sovrascrivere solo per i metodi. La stessa regola non si applica alle variabili. Non è consentito sostituire le variabili, quindi le variabili possono essere solo nascoste (nessuna differenza tra variabile statica o non statica). L'esempio seguente mostra come il metodo getName()viene sovrascritto e la variabile nameè nascosta:

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

In fase di esecuzione, la versione figlio di un metodo sottoposto a override viene sempre eseguita per un'istanza indipendentemente dal fatto che la chiamata al metodo sia definita in un metodo di classe padre o figlio. In questo modo, il metodo genitore non viene mai utilizzato a meno che non si faccia riferimento a una chiamata esplicita al metodo padre, utilizzando la sintassi ParentClassName.method (). In alternativa, in fase di esecuzione la versione genitore di un metodo nascosto viene sempre eseguita se la chiamata al metodo è definita nella classe genitore.


0

In override del metodo , la risoluzione del metodo viene eseguita dalla JVM sulla base dell'oggetto runtime. Mentre nel metodo nascosto, la risoluzione del metodo viene eseguita dal compilatore sulla base del riferimento. Quindi,

Se il codice fosse stato scritto come,

public static void main(String[] args) {
        Animal myCat = new Cat();        
        myCat.testClassMethod();        
    }

L'output sarebbe il seguente:
metodo di classe in Animal.


0

Si chiama nascondersi perché il compilatore nasconde l'implementazione del metodo della super classe, quando la sottoclasse ha lo stesso metodo statico.

Il compilatore non ha visibilità limitata per i metodi sostituiti ed è solo durante il runtime che viene deciso quale viene utilizzato.


0

Questa è la differenza tra sovrascrivere e nascondere:

Animale a = nuovo gatto ();

a.testClassMethod () chiamerà il metodo nella classe genitore poiché è un esempio di metodo nascosto. Il metodo da chiamare è determinato dal tipo di variabile di riferimento e deciso in fase di compilazione.

a.testInstanceMethod () chiamerà il metodo nella classe figlia poiché è un esempio di override del metodo. Il metodo da chiamare è determinato dall'oggetto che viene utilizzato per chiamare il metodo in fase di esecuzione.


0

Penso che questo non sia ancora completamente spiegato. Si prega di vedere il seguente esempio.

class Animal {
    public static void testClassMethod() {
        System.out.println("The static method in Animal");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method in Animal");
    }
}


public class Cat extends Animal {
    public static void testClassMethod() {
        System.out.println("The static method in Cat");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method in Cat");
    }

    public static void main(String[] args) {
        Animal myCat = new Cat();
        Cat myCat2 = new Cat();
        myCat.testClassMethod();
        myCat2.testClassMethod();
        
        
        myCat.testInstanceMethod();
        myCat2.testInstanceMethod();
    }
}

L'output sarà il seguente.

The static method in Animal
The static method in Cat
The instance method in Cat
The instance method in Cat

-1

Come si nasconde il metodo statico in java? La classe Cat sta estendendo la classe Animal. Quindi nella classe Cat saranno presenti entrambi i metodi statici (intendo il metodo statico della classe Child e il metodo statico della classe Parent) Ma in che modo JVM nasconde il metodo statico Parent? Come si comportano in Heap and Stack?


Questa non è una risposta. È l'estensione della domanda posta. Potrebbe essere stata una domanda separata o una parte dei commenti sulla domanda.
Sri9911
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.