Заміна проти приховування Java - заплутана


88

Я збентежений тим, як заміщення відрізняється від приховування в Java. Хтось може надати більше деталей про те, як вони відрізняються? Я прочитав підручник Java, але зразок коду все одно змусив мене розгубитися.

Щоб бути більш зрозумілим, я добре розумію перевизначення. Моя проблема полягає в тому, що я не бачу, як приховування відрізняється, за винятком того факту, що один знаходиться на рівні екземпляра, а інший - на рівні класу.

Переглядаючи підручник з 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.");
    }
}

Тоді ми маємо підклас 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();
    }
}

Тоді вони кажуть:

Результат роботи цієї програми такий:

Метод занять у Тварині.

Метод екземпляра в Cat.

Для мене той факт, що виклик методу класу testClassMethod()безпосередньо з Animalкласу виконує метод у Animalкласі, є досить очевидним, там немає нічого особливого. Потім вони викликають testInstanceMethod()з посилання на myCat, так знову досить очевидно, що метод, який виконується тоді, є методом у екземпляріCat .

З того, що я бачу, приховування дзвінків поводиться так само, як перевизначення, то навіщо робити таку різницю? Якщо я запустив цей код, використовуючи класи вище:

Cat.testClassMethod();

Я отримаю: метод класу в Cat. Але якщо я видалю testClassMethod()з Cat, то отримаю: метод класу в Animal.

Що показує мені, що написання статичного методу з тим самим підписом, що й у батьківському, у підкласі майже перекриває.

Сподіваюся, я даю зрозуміти, де я розгублений і хтось може пролити світло. Спасибі заздалегідь!


Відповіді:


103

Заміна в основному підтримує пізнє зв'язування. Тому під час виконання роботи вирішується, який метод буде викликаний. Це для нестатичних методів.

Приховування призначене для всіх інших членів (статичні методи, члени екземпляра, статичні члени). Він заснований на ранньому зв'язуванні. Більш чітко, метод або член, який буде викликаний або використаний, визначається під час компіляції.

У вашому прикладі перший виклик Animal.testClassMethod()- це виклик staticметоду, отже, ми впевнені, який метод буде викликаний.

У другому виклику, myAnimal.testInstanceMethod()ви викликаєте нестатичний метод. Це те, що ви називаєте поліморфізмом часу виконання. До часу запуску не вирішується, який метод слід викликати.

Для подальших роз’яснень читайте Заміщення проти приховування .


3
Дякую за швидку відповідь, це пояснює це! Я помітив, що у прикладі JavaRanch вони використовували змінну для виклику методу class, а не безпосередньо для використання класу, що полегшує розуміння. Я думаю, у підручнику з Java вони використовували клас безпосередньо, оскільки використання екземпляра для виклику статичного методу, мабуть, не є доброю практикою, але їм слід було використовувати myAnimal.testClassMethod () замість Animal.testClassMethod () .
Lostlinkpr

+1 за те, що він міг правильно записати це словами, а не на прикладі! :)
WhyNotHugo

@Kazekage Gaara Чи є різниця між перевантаженням і приховуванням?
gstackoverflow

1
Звичайно, я погоджуюся з відповіддю, але як щодо цього private methods? Вони не можуть бути, overriddenоскільки підклас не знає про їх існування .. Тому вони можуть бути hiddenзамість цього.
Пасхаліс,

Відмінна відповідь! Хоча ви можете додати приклад на coderanch заради повноти :)
Шубхем Міттал,

19

Статичні методи приховані, нестатичні методи замінені. Різниця помітна, коли дзвінки не кваліфікуються "something ()" vs "this.something ()".

Насправді я не можу сказати це словами, тому ось приклад:

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

}

ВИХІД:

animal.something
dog.eat

1
добре, тоді що станеться, якщо я покличу `dog husky = new dog (); ' і зателефонує husky.Animal();чи надрукує тварина. щось чи собака. щось ? я думаю, НЕПРАВИЛЬНО сказати **, що ** Це завжди буде викликати Animal.something ()
amarnath harish

@amarnathharish Ви не можете зробити .Animal(), пам'ятайте Animal(), це конструктор.
Чувак156

І як додаткові роз'яснення для всіх , хто цікаво, причина , по якій something()в Animal() завжди виклик тваринного something()відбувається тому, що виклик методу статичного рішення під час компіляції , а не під час виконання. Це означає, що виклик статичного методу Animal()завжди імпліцитно викликає Animal.something(). Це досить інтуїтивно, якщо подумати: виклику статичного методу повинно передувати ім'я класу (тобто className.staticMethodName()), якщо виклик не в тому ж класі.
Чувак156,

13

Це різниця між заміщеннями та приховуванням,

  1. Якщо обидва методи в батьківському класі та дочірньому класі є методом екземпляра, це називається заміною.
  2. Якщо обидва методи в батьківському класі та дочірньому класі є статичним методом, це називається приховуванням.
  3. Один із методів не може бути статичним у батьківському та як примірник для дочірнього. і навпаки.

введіть тут опис зображення


3
Ви вирізали та вставили цю таблицю безпосередньо з підручника, який, як сказав ОП, не допоміг йому зрозуміти.
wolfcastle

З таблиці це дуже чітко видно: у прикладах розглядались не всі випадки.
вівторок

3

Якщо я правильно розумію ваше запитання, тоді відповідь "ви вже перевизначаєте".

"Що показує мені, що написання статичного методу з тим самим іменем, що й у батьківському, у підкласі майже перекриває".

Якщо ви пишете метод у підкласі з точно таким самим іменем, як метод у суперкласі, це замінить метод суперкласу. Анотація @Override не потрібна, щоб замінити метод. Однак це робить ваш код більш читабельним і змушує компілятор перевірити, чи ви насправді перевизначаєте метод (і не помилявся, наприклад, метод підкласу).


1
Ця відповідь не вдається розглянути екземпляр порівняно зі статичними методами щодо перевизначення / приховування.
Пол Беллора,

3

Перевизначення відбувається лише з методами екземпляра. Коли тип посилальної змінної - Animal, а об’єкт - Cat, тоді метод екземпляра викликається з Cat (це замінює). Для того самого об'єкта acat використовується метод класу Animal.

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

}

Вихід:

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

Правило запам'ятовування просте: метод у класі, що розширюється, не може змінити static на void і не може змінити void на static. Це спричинить помилку компіляції.

Але якщо void Nameйого змінили наvoid Name "Заміщення".

І якщо static Nameйого змінили на static NameHiding. (Залежно від типу посилання, що використовується для виклику методу, можна викликати як статичний метод підкласу, так і метод суперкласу.)


1

У цьому фрагменті коду я використовую модифікатор доступу "private" замість "static", щоб показати вам різницю між методами приховування та перевизначення методів.

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

Вихід:

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

дивуюсь, чому він не отримав "за". дуже вдячна відповідь.
amarnath harish

0

На основі моїх останніх досліджень Java

  • перевизначення методу , коли підклас має той самий метод з однаковим підписом у підкласі.
  • Приховування методу , коли підклас має однакову назву методу, але інший параметр. У цьому випадку ви не перевизначаєте батьківський метод, а приховуєте його.

Приклад з книги OCP Java 7, стор. 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
  }
}

але якщо ми напишемо наступне головне:

  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
  }

У другому основному ми використовуємо клас Object як статичний тип, тому, коли ми викликаємо рівний метод в об'єкті Point, він чекає, що клас Point прийде як параметр, але Object прийде. Отже, клас Object дорівнює методу отримання запуску, оскільки ми маємо рівність (Object o). У цьому випадку клас Пойнти дорівнює не перевизначає, але приховує метод Об'єкт класу дорівнює .


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

Пов’язана сторінка підручника Java пояснює концепцію перевизначення та приховування

Метод екземпляра в підкласі з однаковою сигнатурою (ім'я, плюс номер та тип його параметрів) і тип повернення, як метод екземпляра в суперкласі, замінює метод суперкласу.

Якщо підклас визначає статичний метод із тією ж сигнатурою, що і статичний метод у суперкласі, тоді метод у підкласі приховує метод у суперкласі.

Різниця між приховуванням статичного методу та перевизначенням методу екземпляра має важливі наслідки:

  1. Версія методу перевизначеного екземпляра, який викликається, є версією у підкласі.
  2. Версія прихованого статичного методу, який викликається, залежить від того, чи викликається він із суперкласу чи підкласу.

Повертаючись до вашого прикладу:

Animal myAnimal = myCat;

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

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

Наведене вище твердження ще не показує приховування.

Тепер змініть код, як показано нижче, щоб отримати інший результат:

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

Я намагаюся зрозуміти, що означає слово «приховування». Що приховує що? Чи прихований статичний метод у батьківському класі, оскільки (найменш очікувано) його викликають? Або статичний метод у класі Child прихований, оскільки його не викликають?
скорпіон

0

На додаток до перелічених вище прикладів, ось невеликий зразок коду для уточнення відмінності між приховуванням та заміною:

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

Виклик child.printParent()виходів:
бути прихованим: Батько,
який потрібно замінити: Дочірній

Виклик child.printChild()виходів:
бути прихованим: Дочірній матеріал,
який потрібно перевизначити: Дочірній

Як ми можемо побачити з виведених вище результатів (особливо виділених жирним шрифтом виходів), метод приховування поводиться інакше, ніж перевизначення.

Java дозволяє як приховувати, так і замінювати лише методи. Це ж правило не застосовується до змінних. Заміна змінних не дозволяється, тому змінні можуть бути лише прихованими (немає різниці між статичною та нестатичною змінною). У наведеному нижче прикладі показано, як метод getName()перевизначено, а змінну nameприховано:

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

Під час виконання дочірня версія перевизначеного методу завжди виконується для екземпляра, незалежно від того, чи визначено виклик методу в методі батьківського або дочірнього класу. Таким чином, батьківський метод ніколи не використовується, якщо не вказано явний виклик батьківського методу, використовуючи синтаксис ParentClassName.method (). Як варіант, під час виконання батьківська версія прихованого методу завжди виконується, якщо виклик методу визначений у батьківському класі.


0

У методі перевизначення методу роздільна здатність методу виконується JVM на основі об'єкта виконання. Тоді як при приховуванні методу роздільна здатність методу виконується компілятором на основі посилань. Таким чином,

Якби код писався як,

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

Результат буде таким, як показано нижче:
метод класу у тварини.


0

Це називається приховуванням, оскільки компілятор приховує реалізацію методу суперкласу, коли підклас має той самий статичний метод.

Компілятор не має обмеженої видимості для перевизначених методів, і лише під час роботи вирішується, який із них використовується.


0

Ось різниця між перевизначенням та приховуванням:

Тварина a = новий Кіт ();

a.testClassMethod () викличе метод у батьківському класі, оскільки це приклад приховування методу. Викликаний метод визначається типом посилальної змінної та приймається під час компіляції.

a.testInstanceMethod () викличе метод у дочірньому класі, оскільки це приклад перевизначення методу. Викликаний метод визначається об’єктом, який використовується для виклику методу під час виконання.


-1

Як відбувається приховування статичного методу в Java? Клас котів розширює клас тварин. Отже, у класі Cat будуть обидва статичні методи (я маю на увазі статичний метод дочірнього класу та статичний метод батьківського класу) Але як JVM приховує батьківський статичний метод? Як справа в Heap and Stack?


Це не відповідь. Це продовження поставленого питання. Можливо, це було окреме питання або частина коментарів до цього питання.
Sri9911,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.