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 Animal
wykonanie metody w Animal
klasie, 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ę!
źródło
Odpowiedzi:
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łaniemstatic
metody, 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 .
źródło
private methods
? Nie mogą być,overridden
ponieważ podklasa nie wie o ich istnieniu .. Dlatego mogąhidden
zamiast tego być .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:
źródło
husky.Animal();
, czy wydrukuje zwierzę. coś czy pies. coś ? myślę, że NIEPRAWIDŁOWO powiedzieć ** that ** To zawsze będzie wywoływać Animal.something ().Animal()
, pamiętaj, żeAnimal()
jest konstruktorem.something()
w wywołaniuAnimal()
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 wAnimal()
zawsze wywołuje niejawnieAnimal.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.To jest różnica między nadpisywaniem a ukrywaniem,
źródło
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).
źródło
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:
źródło
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 Name
zostanie zmienionyvoid Name
na Zastępowanie.A jeśli
static Name
zostanie zmienionystatic Name
na 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).źródło
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:
źródło
Na podstawie moich ostatnich badań dotyczących języka Java
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 .
źródło
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())
źródło
Połączona strona samouczka Java wyjaśnia koncepcję zastępowania i ukrywania
Rozróżnienie między ukrywaniem metody statycznej a przesłanianiem metody instancji ma ważne konsekwencje:
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();
źródło
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 zmiennaname
jest 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; } }
źródło
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.
źródło
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.
źródło
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.
źródło
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.
źródło
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?
źródło