Можно ли переопределить статический метод в производном классе?

У меня есть статический метод, определенный в базовом классе, я хочу переопределить этот метод в его дочернем классе, возможно ли это?

Я пробовал это, но это не сработало, как я ожидал. Когда я создал экземпляр класса B и вызывается его метод callMe(), вызывается статический метод foo() в классе A.

public abstract class A {
  public static void foo() {
    System.out.println("I am base class");
  }

  public void callMe() {
    foo();
  }
}

Public class B {
  public static void foo() {
      System.out.println("I am child class");
  }
}

Ответ 1

Статические вызовы методов разрешаются во время компиляции (без динамической отправки).

class main {
    public static void main(String args[]) {
            A a = new B();
            B b = new B();
            a.foo();
            b.foo();
            a.callMe();
            b.callMe();
    }
}
abstract class A {
    public static void foo() {
        System.out.println("I am superclass");
    }

    public void callMe() {
        foo(); //no late binding here; always calls A.foo()
    }
}

class B extends A {
    public static void foo() {
        System.out.println("I am subclass");
    }
}

дает

I am superclass
I am subclass
I am superclass
I am superclass

Ответ 2

Can I override a static method?

Многие слышали, что вы не можете переопределить статический метод. Это правда - вы не можете. Однако можно написать такой код:

class Foo {
    public static void method() {
        System.out.println("in Foo");
    }
}

class Bar extends Foo {
    public static void method() {
        System.out.println("in Bar");
    }
}

Это компилируется и работает отлично. Разве это не пример статического метода, который переопределяет другой статический метод? Ответ - нет - это пример статического метода, скрывающего другой статический метод. Если вы попытаетесь переопределить статический метод, компилятор фактически не остановит вас - он просто не делает того, что, по вашему мнению, делает.

Какая разница?

Вкратце, когда вы переопределяете метод, вы все равно получаете преимущества полиморфизма во время выполнения, а когда вы скрываете, вы этого не делаете. Так что это значит? Взгляните на этот код:

class Foo {
    public static void classMethod() {
        System.out.println("classMethod() in Foo");
    }

    public void instanceMethod() {
        System.out.println("instanceMethod() in Foo");
    }
}

class Bar extends Foo {
    public static void classMethod() {
        System.out.println("classMethod() in Bar");
    }

    public void instanceMethod() {
        System.out.println("instanceMethod() in Bar");
    }
}

class Test {
    public static void main(String[] args) {
        Foo f = new Bar();
        f.instanceMethod();
        f.classMethod();
    }
}

Если вы запустите это, выход

instanceMethod() в баре
classMethod() в Foo

Почему мы получаем instanceMethod из Bar, но classMethod() из Foo? Разве мы не используем один и тот же экземпляр f для доступа к ним обоих? Да, мы - но так как один является переопределяющим, а другой скрывается, мы видим другое поведение.

Так как instanceMethod() является (барабанным валиком, пожалуйста...) методом экземпляра, в котором бар переопределяет метод из Foo, во время выполнения JVM использует фактический класс экземпляра f для определения того, какой метод запускать. Хотя f был объявлен как Foo, фактический экземпляр, который мы создали, был новым Bar(). Таким образом, во время выполнения JVM обнаруживает, что f является экземпляром Bar, и поэтому он вызывает instanceMethod() в Bar, а не в Foo. Это то, что Java обычно работает, например, методами.

Вместе с classMethod(). поскольку (гм) это метод класса, компилятор и JVM не ожидают, что для вызова метода будет задействован фактический экземпляр. И даже если вы предоставите один (который мы сделали: экземпляр, упомянутый f), JVM никогда не будет смотреть на него. Компилятор будет рассматривать только объявленный тип ссылки и использовать этот объявленный тип для определения во время компиляции метода для вызова. Поскольку f объявлен как тип Foo, компилятор просматривает f.classMethod() и решает, что это означает Foo.classMethod. Не имеет значения, что экземпляр, присвоенный f, фактически является баром - для статических методов компилятор использует только объявленный тип ссылки. То, что мы имеем в виду, когда мы говорим, что статический метод не имеет полиморфизма во время выполнения.

Поскольку методы экземпляра и методы класса имеют это важное различие в поведении, мы используем разные термины - "переопределение", например, методов и "скрытие" для методов класса - чтобы различать эти два случая. И когда мы говорим, что вы не можете переопределить статический метод, это означает, что даже если вы пишете код, который выглядит как переопределяющий статический метод (например, первый Foo и Bar в верхней части этой страницы) - это не будет ведут себя как переопределенный метод. для более ссылки this

Ответ 3

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

Ответ 4

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

В случае статических методов нет объекта для использования, чтобы рассказать вам, какую реализацию использовать. Это означает, что компилятор может использовать только объявленный тип, чтобы выбрать реализацию метода для вызова.

Ответ 5

Нет. Это невозможно.

Некоторые похожие (не одинаковые) вопросы здесь и здесь.

Ответ 6

Статические методы разрешаются во время компиляции. Поэтому, если вы хотите вызвать метод родительского класса, вы должны явно вызвать классName или экземпляр, как показано ниже.

A.foo();

или

 A a = new A();
 a.foo();

Ответ 7

В двух словах статический метод переопределения не является полиморфизмом, он "скрывает метод". Когда вы переопределяете статический метод, у вас не будет доступа к методу базового класса, поскольку он будет скрыт производным классом. Использование super() вызовет ошибку времени компиляции.