Вопрос по java, methods, inheritance, override, hiding – Переопределение против сокрытия Java - Confused

66

Я не совсем понимаю, как переопределение отличается от скрытия в Java. Кто-нибудь может предоставить более подробную информацию о том, как они отличаются? Я прочитал Java Учебник но пример кода все еще оставил меня в замешательстве.

Чтобы быть более понятным, я хорошо понимаю, что такое Overriding. Моя проблема в том, что я не вижу, чтобы скрытие отличалось, за исключением того факта, что один находится на уровне экземпляра, а другой - на уровне класса.

Смотрим на учебный код 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.");
    }
}

Тогда у нас есть кошка подкласса:

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

Затем они говорят:

Выход из этой программы выглядит следующим образом:

Классный метод в Animal.

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

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

Из того, что я вижу, скрытие вызова ведет себя как переопределение, так зачем делать это различие. Если я запускаю этот код, используя классы выше:

Cat.testClassMethod();

Я получу: Метод класса в Cat. Но если я удалю testClassMethod () из Cat, я получу: Метод класса в Animal.

Что показывает мне, что написание статического метода с той же сигнатурой, что и у родителя, в подклассе в значительной степени выполняет переопределение.

Надеюсь, я проясню, где я в замешательстве, и кто-то может пролить свет. Большое спасибо заранее

Ваш Ответ

13   ответов
3

то ответ «вы уже переопределяете».

«Что показывает мне, что написание статического метода с тем же именем, что и у родителя, в подклассе в значительной степени переопределяет.»

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

Этот ответ не относится к экземпляру против статических методов в отношении переопределения / скрытия. Paul Bellora
88

какой метод будет вызван, определяется во время выполнения. Он предназначен для нестатических методов. Скрытие предназначено для всех других членов (статические методы, члены экземпляра, статические члены). Он основан на раннем связывании. Более ясно, метод или член, который будет вызван или использован, определяется во время компиляции.

В твоем примере первый звонок,Animal.testClassMethod() это вызовstatic метод, следовательно, он довольно уверен в том, какой метод будет вызван.

Во втором звонке,myAnimal.testInstanceMethod(), он вызывает нестатический метод. Это то, что вы называете полиморфизмом во время выполнения. Пока не решено, какой метод вызывать, пока не решен

Для дальнейшего уточнения прочитайтеэт.

@ Kazekage Gaara Есть ли разница между перегрузкой и сокрытием? gstackoverflow
+ 1 за умение правильно выразить словами, а не примером! :) WhyNotHugo
Я, конечно, согласен с ответом, но как насчетprivate methods? Они не могут бытьoverridden поскольку подкласс не знает об их существовании .. Поэтому они могут бытьhidden вместо. Paschalis
Спасибо за быстрый ответ, это проясняет! Я заметил, что в примере JavaRanch они использовали переменную для вызова метода класса вместо непосредственного использования класса, что облегчает понимание. Я предполагаю, что в руководстве по Java они использовали класс напрямую, потому что использование экземпляра для вызова статического метода, вероятно, не очень хорошая практика, но они должны были использовать MyAnimal.testClassMethod () вместо того Animal.testClassMethod (). Lostlinkpr
Отличный ответ! Хотя вы можете добавить пример в coderanch для полноты:) Shubham Mittal
2
public class First {

public void Overriding(int i) {  // will be overrided 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) {  // overrided here  }

public static void Hiding(int i) {  // hidden
                                    // because it's static } 
}

метод в расширяющем классе не может изменить статический тип на void и не может заменить void на статический. Это приведет к ошибке компиляции.

Но если пустое имя изменился на пустое имя это переопределение.

И если статическое имя изменился на статическое имя это скрывается. (Когда компилятор видит Статические метод в объекте суперкласса, тогда он не проверяет метод в подклассе.)

11

Если и метод в родительском классе, и дочерний класс являются методом экземпляра, он называется переопределением. Если оба метода в родительском и дочернем классах являются статическими, это называется скрытием. Один метод не может быть статическим в родительском и как экземпляр в дочернем. и наоборот.

Таблица очень наглядно показывает, в примерах не все случаи были рассмотрены. tutak
Вы вырезали и вставили этот стол прямо из учебника, который, по словам ОП, не помог ему понять. wolfcastle
0

полняется для экземпляра независимо от того, определен ли вызов метода в методе родительского или дочернего класса. Таким образом, родительский метод никогда не используется, если на него не ссылается явный вызов родительского метода с использованием синтаксиса ParentClassName.method (). Кроме того, во время выполнения родительская версия скрытого метода всегда выполняется, если вызов метода определен в родительском классе.

16

нестатические методы переопределяются. Разница заметна, когда вызовы не квалифицированы как "что-то ()" против "это.кома ()".

Я не могу выразить это словами, так что вот пример:

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
ok, тогда что произойдет, если я назову `dog husky = new dog (); ' и звонитеhusky.Animal(); будет ли печатать Animal.something или Dog.something? я думаю, это НЕПРАВИЛЬНО сказать **, что ** Это всегда будет вызывать Animal.something () amarnath harish
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.
0

класс Animal. Таким образом, в классе Cat будут оба статических метода (я имею в виду статический метод дочернего класса и статический метод родительского класса). Но как JVM скрывает статический метод родительского класса? Как это работает в куче и стеке?

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

method Наиважнейшая, когда у подкласса есть тот же метод с той же самой подписью в подклассе. Методпрячетс, когда подкласс имеет то же имя метода, но другой параметр. В этом случае вы не переопределяете родительский метод, а скрываете его.

Пример из книги 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
  }

Во втором main мы используем класс Object в качестве статического типа, поэтому, когда мы вызываем равный метод в объекте Point, он ожидает, что класс Point поступит в качестве параметра, но объект придет. Итак, класс Object запускается методом equals, потому что у нас есть equals (Object o). В этом случае класс Точки равен не переопределяет, но скрывает метод класса Object equals.

0

я и скрытия

Метод экземпляра в подклассе с той же сигнатурой (имя, плюс номер и тип его параметров) и типом возврата, который используется в качестве метода экземпляра в суперклассе, переопределяет метод суперкласса.

Если подкласс определяет статический метод с той же сигнатурой, что и статический метод в суперклассе, то метод в подклассе скрывает метод в суперклассе.

Различие между сокрытием статического метода и переопределением метода экземпляра имеет важные последствия:

Версия переопределенного метода экземпляра, который вызывается, является версией в подклассе. Версия скрытого статического метода, который вызывается Зависит от того, вызывается ли он из суперкласса или подкласса.

Возвращаясь к вашему примеру:

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

то «статический», чтобы показать вам разницу между методами скрытия и переопределением.

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

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

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

Похожие вопросы