Przesłanianie a ukrywanie Javy - zdezorientowana


88

Nie wiem, jak zastępowanie różni się od ukrywania w Javie. Czy ktoś może podać więcej szczegółów na temat różnic między nimi? Przeczytałem samouczek Java, ale przykładowy kod nadal mnie zdezorientował.

Żeby było jaśniej, dobrze rozumiem nadpisywanie. Mój problem polega na tym, że nie widzę, czym różni się ukrywanie, z wyjątkiem faktu, że jeden jest na poziomie instancji, a drugi na poziomie klasy.

Patrząc na kod tutoriala 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.");
    }
}

Następnie mamy podklasę 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();
    }
}

Następnie mówią:

Dane wyjściowe tego programu są następujące:

Metoda klasowa w Animal.

Metoda instancji w Cat.

Dla mnie fakt, że wywołanie metody klasowej testClassMethod()bezpośrednio z klasy powoduje Animalwykonanie metody w Animalklasie, jest dość oczywisty, nie ma w tym nic specjalnego. Następnie wywołują metodę testInstanceMethod()from a reference do myCat, więc znowu całkiem oczywiste, że metoda wykonywana wtedy jest tą w instancji Cat.

Z tego, co widzę, ukrywanie wywołania zachowuje się jak nadpisywanie, więc po co to rozróżniać? Jeśli uruchomię ten kod przy użyciu powyższych klas:

Cat.testClassMethod();

Otrzymam: Metoda klasowa w Cat. Ale jeśli usunę testClassMethod()z Cat, otrzymam: Metoda klasy w Animal.

Co pokazuje mi, że pisanie metody statycznej, z taką samą sygnaturą jak w klasie nadrzędnej, w podklasie w zasadzie powoduje nadpisanie.

Mam nadzieję, że wyjaśniam, gdzie jestem zdezorientowany i ktoś może rzucić trochę światła. Z góry bardzo dziękuję!


Odpowiedzi:


103

Zastępowanie zasadniczo obsługuje późne wiązanie. Dlatego w czasie wykonywania zdecydowano, która metoda zostanie wywołana. Dotyczy metod niestatycznych.

Ukrywanie dotyczy wszystkich innych członków (metody statyczne, składowe instancji, elementy statyczne). Opiera się na wczesnym wiązaniu. Bardziej wyraźnie, metoda lub element członkowski, który ma być wywołany lub używany, jest określany w czasie kompilacji.

W twoim przykładzie pierwsze wywołanie Animal.testClassMethod()jest wywołaniem staticmetody, stąd jest całkiem pewne, która metoda zostanie wywołana.

W drugim wywołaniu myAnimal.testInstanceMethod()wywołujesz metodę niestatyczną. Nazywa się to polimorfizmem w czasie wykonywania. Decyzja o wywołaniu metody jest podejmowana dopiero w czasie wykonywania.

Aby uzyskać dalsze wyjaśnienia, przeczytaj artykuł Przesłanianie i ukrywanie .


3
Dziękuję za szybką odpowiedź, to wyjaśnia! Zauważyłem, że w przykładzie JavaRanch użyli zmiennej do wywołania metody klasy zamiast bezpośredniego używania klasy, co ułatwia zrozumienie. Wydaje mi się, że w samouczku Java użyli tej klasy bezpośrednio, ponieważ użycie instancji do wywołania metody statycznej prawdopodobnie nie jest dobrą praktyką, ale powinni użyć myAnimal.testClassMethod () zamiast Animal.testClassMethod () .
Lostlinkpr

+1 za umiejętność właściwego wyrażenia tego słowami, a nie przykładem! :)
WhyNotHugo

@Kazekage Gaara Czy jest różnica między przeciążeniem a ukrywaniem?
gstackoverflow

1
Oczywiście zgadzam się z odpowiedzią, ale co powiesz na to private methods? Nie mogą być, overriddenponieważ podklasa nie wie o ich istnieniu .. Dlatego mogą hiddenzamiast tego być .
Paschalis,

Doskonała odpowiedź! Chociaż możesz dodać przykład w coderanch ze względu na kompletność :)
Shubham Mittal

19

Metody statyczne są ukryte, metody niestatyczne są nadpisywane. Różnica jest zauważalna, gdy wywołania nie są kwalifikowane „coś ()” a „to.something ()”.

Wydaje się, że nie mogę odłożyć tego na słowa, więc oto przykład:

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

}

WYNIK:

animal.something
dog.eat

1
ok, co by się stało, gdybym zadzwonił do `psa husky = nowy pies (); ' i zawołaj husky.Animal();, czy wydrukuje zwierzę. coś czy pies. coś ? myślę, że NIEPRAWIDŁOWO powiedzieć ** that ** To zawsze będzie wywoływać Animal.something ()
amarnath harish

@amarnathharish Nie możesz tego zrobić .Animal(), pamiętaj, że Animal()jest konstruktorem.
Dude156

I jako dalsze wyjaśnienie dla każdego, kto się zastanawia, powodem, dla którego something()w wywołaniu Animal() zawsze jest Animal's, something()jest to, że wywołanie metody statycznej jest rozwiązywane w czasie kompilacji, a nie w czasie wykonywania. Oznacza to, że wywołanie metody statycznej w Animal()zawsze wywołuje niejawnie Animal.something(). Jeśli się nad tym zastanowić, jest to dość intuicyjne: wywołanie metody statycznej musi być poprzedzone nazwą klasy (tj. className.staticMethodName()), Chyba że wywołanie należy do tej samej klasy.
Dude156

14

To jest różnica między nadpisywaniem a ukrywaniem,

  1. Jeśli obie metody w klasie nadrzędnej i podrzędnej są metodą instancji, wywołuje ona przesłonięcia.
  2. Jeśli obie metody w klasie nadrzędnej i podrzędnej są metodą statyczną, nazywa się to ukrywaniem.
  3. Jedna metoda nie może być statyczna w rodzicu i jako instancja w dziecku. i na odwrót.

wprowadź opis obrazu tutaj


3
Wytniesz i wkleiłeś tę tabelę bezpośrednio z samouczka, o którym OP powiedział, że nie pomógł mu zrozumieć.
wolfcastle

Z tabeli jasno wynika, że ​​w przykładach nie uwzględniono wszystkich przypadków.
tutak

3

Jeśli dobrze rozumiem Twoje pytanie, odpowiedź brzmi: „Już zastępujesz”.

„Co pokazuje mi, że pisanie metody statycznej o tej samej nazwie, co w klasie nadrzędnej, w podklasie prawie powoduje nadpisanie”.

Jeśli napiszesz metodę w podklasie o dokładnie takiej samej nazwie, jak metoda w nadklasie, zastąpi ona metodę nadklasy. Adnotacja @Override nie jest wymagana do przesłonięcia metody. Czyni to jednak kod bardziej czytelnym i zmusza kompilator do sprawdzenia, czy faktycznie nadpisujesz metodę (i na przykład nie przeliterowałeś metody podklasy).


1
Ta odpowiedź nie odnosi się do instancji w porównaniu do metod statycznych w odniesieniu do przesłaniania / ukrywania.
Paul Bellora

3

Zastępowanie ma miejsce tylko w przypadku metod instancji. Gdy typ zmiennej referencyjnej to Animal, a obiekt to Cat, metoda instancji jest wywoływana z Cat (jest to nadpisanie). Dla tego samego obiektu acat używana jest metoda klasy Animal.

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

}

Wynik to:

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 */ } 
}

Zasada zapamiętywania jest prosta: metoda w klasie rozszerzającej nie może zmienić static na void i nie może zmienić void na static. Spowoduje to błąd kompilacji.

Ale jeśli void Namezostanie zmieniony void Namena Zastępowanie.

A jeśli static Namezostanie zmieniony static Namena Ukrywanie. (Można wywołać zarówno statyczną metodę podklasy, jak i metodę nadklasy, w zależności od typu odwołania użytego do wywołania metody).


1

W tym fragmencie kodu używam modyfikatora dostępu „private” zamiast „static”, aby pokazać różnicę między metodami ukrywania i nadpisywania.

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

Wynik:

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

Zastanawiam się, dlaczego nie dostał pozytywnych głosów… bardzo ceniona odpowiedź.
amarnath harish

0

Na podstawie moich ostatnich badań dotyczących języka Java

  • przesłanianie metody , gdy podklasa ma tę samą metodę z tym samym podpisem w podklasie.
  • Ukrywanie metody , gdy podklasa ma tę samą nazwę metody, ale inny parametr. W takim przypadku nie zastępujesz metody nadrzędnej, ale ją ukrywasz.

Przykład z książki OCP Java 7, strony 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
  }
}

ale jeśli napiszemy następujące główne:

  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
  }

W drugiej części głównej używamy klasy Object jako typu statycznego, więc kiedy wywołujemy metodę equal w obiekcie Point, czeka na pojawienie się klasy Point jako parametru, ale nadchodzi Object. Czyli uruchamiana jest metoda Object class equals, ponieważ mamy tam equals (Object o). W tym przypadku klasa Point jest równa dosen't overrides, ale ukrywa metodę equals klasy Object .


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

Połączona strona samouczka Java wyjaśnia koncepcję zastępowania i ukrywania

Metoda instancji w podklasie z tą samą sygnaturą (nazwa plus numer i typ jej parametrów) i typem zwracanym jako metoda instancji w nadklasie przesłania metodę nadklasy.

Jeśli podklasa definiuje metodę statyczną z taką samą sygnaturą, jak metoda statyczna w nadklasie, wówczas metoda w podklasie ukrywa metodę z nadklasy.

Rozróżnienie między ukrywaniem metody statycznej a przesłanianiem metody instancji ma ważne konsekwencje:

  1. Wywoływana jest wersja metody przesłoniętej instancji, która znajduje się w podklasie.
  2. Wywoływana wersja ukrytej metody statycznej zależy od tego, czy jest ona wywoływana z nadklasy, czy z podklasy.

Wracając do twojego przykładu:

Animal myAnimal = myCat;

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

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

Powyższe stwierdzenie jeszcze się nie ukrywa.

Teraz zmień kod jak poniżej, aby uzyskać inne dane wyjściowe:

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

Próbuję zrozumieć, co oznacza słowo „ukrywanie się”. Co skrywa co? Czy metoda statyczna w klasie Parent jest ukryta, ponieważ (najmniej oczekiwana) jest wywoływana? A może metoda statyczna w klasie Child jest ukryta, ponieważ nie jest wywoływana?
skorpion

0

Oprócz przykładów wymienionych powyżej, oto mały przykładowy kod wyjaśniający różnicę między ukrywaniem a zastępowaniem:

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

Wywołanie child.printParent()wyjść:
ukryć: nadpisany rodzic
: dziecko

Wywołanie child.printChild()wyjść:
do ukrycia: Dziecko
do nadpisania: Dziecko

Jak widać z powyższych wyników (zwłaszcza wyników oznaczonych pogrubioną czcionką), ukrywanie metody zachowuje się inaczej niż przesłanianie.

Java umożliwia zarówno ukrywanie, jak i przesłanianie tylko dla metod. Ta sama zasada nie dotyczy zmiennych. Zastępowanie zmiennych jest niedozwolone, więc zmienne można tylko ukrywać (nie ma różnicy między zmiennymi statycznymi i niestatycznymi). Poniższy przykład pokazuje, jak metoda getName()jest zastępowana, a zmienna namejest ukryta:

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

W czasie wykonywania wersja podrzędna metody zastępowanej jest zawsze wykonywana dla instancji, niezależnie od tego, czy wywołanie metody jest zdefiniowane w metodzie klasy nadrzędnej czy podrzędnej. W ten sposób metoda nadrzędna nigdy nie jest używana, chyba że przywołuje się jawne wywołanie metody nadrzędnej za pomocą składni ParentClassName.method (). Alternatywnie, w czasie wykonywania wersja nadrzędna metody ukrytej jest zawsze wykonywana, jeśli wywołanie metody jest zdefiniowane w klasie nadrzędnej.


0

W przypadku nadpisywania metody, rozwiązanie metody jest wykonywane przez maszynę JVM na podstawie obiektu runtime. Podczas gdy w ukrywaniu metody, rozdzielczość metody jest wykonywana przez kompilator na podstawie referencji. A zatem,

Gdyby kod został napisany jako,

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

Wynik byłby następujący:
Metoda klas w zwierzęciu.


0

Nazywa się to ukrywaniem, ponieważ kompilator ukrywa implementację metody superklasy, gdy podklasa ma tę samą metodę statyczną.

Kompilator nie ma ograniczonej widoczności dla zastąpionych metod i tylko w czasie wykonywania decyduje, która z nich jest używana.


0

Oto różnica między nadpisywaniem a ukrywaniem:

Zwierzę a = new Cat ();

a.testClassMethod () wywoła metodę w klasie nadrzędnej, ponieważ jest to przykład ukrywania metody. Metoda, która ma zostać wywołana, jest określana przez typ zmiennej referencyjnej i określana w czasie kompilacji.

a.testInstanceMethod () wywoła metodę w klasie potomnej, ponieważ jest to przykład przesłaniania metody. Metoda, która ma zostać wywołana, jest określana przez obiekt, który jest używany do wywołania metody w czasie wykonywania.


-1

Jak ukrywa się statyczna metoda w Javie? Klasa Cat jest rozszerzeniem klasy Animal. Tak więc klasa Cat będzie miała obie metody statyczne (mam na myśli metodę statyczną klasy Child i metodę statyczną klasy Parent). Ale jak JVM ukrywa statyczną metodę nadrzędną? Jak radzi sobie w Heap and Stack?


To nie jest odpowiedź. To rozszerzenie zadanego pytania. Być może samo w sobie było osobnym pytaniem lub częścią komentarzy do pytania.
Sri9911
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.