Почему и когда необходимо повторно объявить статический метод в производном классе, если его определение не может быть переопределено / изменено производным классом? - PullRequest
0 голосов
/ 24 апреля 2019

Я знаком с ключевым словом static и его использованием.Я понимаю, что метод static может быть повторно объявлен в подклассе, но его определение становится скрытым и остается таким же, как и у родительского класса.Я упоминаю некоторые ссылки статей, которые я уже прочитал:

https://www.geeksforgeeks.org/can-we-overload-or-override-static-methods-in-java/

Почему Java не допускает переопределение статических методов?

Разница между статическим и окончательным?

Когда производный класс определяет статический метод с той же сигнатурой, что и статический метод в базовом классе, метод в производном классе скрывает методв базовом классе.Но все же метод базового класса называется, например, display() метод базового класса.

Но мне любопытно, почему и когда необходимо повторно объявить static метод base classв производном классе, если его определение не может быть переопределено / изменено в производном классе, и вместо этого отображается определение базового класса?

    /* Java program to show that if static method is redefined by 
a derived class, then it is not overriding. */

// Superclass 
class Base { 

    // Static method in base class which will be hidden in subclass 
    public static void display() { 
        System.out.println("Static or class method from Base"); 
    } 

    // Non-static method which will be overridden in derived class 
    public void print() { 
        System.out.println("Non-static or Instance method from Base"); 
    } 
} 

// Subclass 
class Derived extends Base { 

    // This method hides display() in Base 
    public static void display() { 
        System.out.println("Static or class method from Derived"); 
    } 

    // This method overrides print() in Base 
    public void print() { 
        System.out.println("Non-static or Instance method from Derived"); 
} 
} 

// Driver class 
public class Test { 
    public static void main(String args[ ]) { 
    Base obj1 = new Derived(); 

    // As per overriding rules this should call to class Derive's static 
    // overridden method. Since static method can not be overridden, it 
    // calls Base's display() 
    obj1.display(); 

    // Here overriding works and Derive's print() is called 
    obj1.print();    
    } 
} 

Ответы [ 2 ]

6 голосов
/ 24 апреля 2019

Основной вывод здесь: не используйте экземпляр переменные для вызова static функций.Java допускает это, но это сбивает с толку, потому что похоже на вызов метода экземпляра, когда это не так.

Эта строка создает экземпляр Derived и присваивает его переменной Base:

Base obj1 = new Derived(); 

Позже, когда вы наберете:

obj1.display();

, на самом деле произойдет:

Base.display();

, потому что obj - это переменная типа Base и display - это метод static.На самом деле, если вы посмотрите на скомпилированный байт-код, вы увидите, что это буквально этот вызов, переменная экземпляра нигде не упоминается:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Derived
       3: dup
       4: invokespecial #3                  // Method Derived."":()V
       7: astore_1
       8: aload_1
       9: pop
      10: invokestatic  #4                  // Method Base.display:()V
      13: aload_1
      14: invokevirtual #5                  // Method Base.print:()V
      17: return
}

Обратите внимание на разницу между obj1.display() вызов

      10: invokestatic  #4                  // Method Base.display:()V

и obj1.print() вызов:

      13: aload_1
      14: invokevirtual #5                  // Method Base.print:()V

static вызов не проталкивает obj1 в стек, ииспользует invokestatic.Вызов экземпляра делает push obj1 в стек (aload_1) и вызывает invokevirtual.

Если вы объявили obj1 типа Derived, вы быувидеть противоположное поведение.С:

Derived obj1 = new Derived(); 

затем

obj1.display();

выходами:

Static or class method from Derived

, потому что это действительно:

Derived.display();

Это все о тип переменной экземпляра.Тип самого экземпляра вообще не имеет значения (на самом деле obj1 может быть null).

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

Я понимаю, что метод static может быть повторно объявлен в подклассе, но его определение скрыто и остается таким же, как и у родительского класса.

...

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

Эти два утверждения противоречат друг другу.Отношение таково: производный метод static переопределяет базовый метод static при обращении через производный класс.Итак, с вашим кодом Base.display ссылки Base.display и Derived.display ссылки Derived.display.Тот факт, что display на Derived не влияет на тот на Base, который все еще доступен через Base (или переменную типа Base).(Если бы у вас не было display в Derived, Derived.display будет ссылаться на Base.display, но, поскольку у вас есть, это не так.)

Но мне любопытнопочему и когда необходимо повторно объявить статический метод базового класса в производном классе ...

Единственное время, которое вам нужно, это если вы хотите Derived.display (или obj1.display, когдаobj1 объявлен как Derived) для выполнения действий, отличных от Base.display (или obj1.display, когда obj1 объявлен как Base).Один пример, который я могу вспомнить, это методы, которые создают экземпляры, возможно, с помощью шаблона Builder:

Base b = Base.builder()
         .setName("the name")
         .setAnswer(42)
         .setBiz("baz")
         .build();

vs.

Derived d = Derived.builder()
         .setName("the name")
         .setAnswer(42)
         .setBiz("baz")
         .setDerivedThingy("whatever")
         .build();
1 голос
/ 24 апреля 2019

Нет необходимости в экземпляре при вызове статического члена или метода.

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

Пример 15.11.1-2.Переменная получателя не имеет значения для статического доступа к полю

Следующая программа прояснит ваши сомнения. Она демонстрирует, что нулевая ссылка также может быть использована для доступа к переменной класса (статической), не вызывая исключение пустого указателя:

class Test {
    static String name = "Jaspreet";
    static Test method1(){
        System.out.print("Hello world");
        return null;
    }
    public static void main(String[] args) {
        System.out.println(method1().name);
    }
}

И анализ того, почему это происходит

Несмотря на то, что метод method1 () равен null, исключение NullPointerException не генерируется.То, что «Hello world» напечатано, демонстрирует, что первичное выражение действительно полностью вычисляется во время выполнения, несмотря на тот факт, что только его тип, а не его значение, используется для определения того, к какому полю обращаться (поскольку имя поля является статическим).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...