Можно ли переопределить статический метод в производном классе?
У меня есть статический метод, определенный в базовом классе, я хочу переопределить этот метод в его дочернем классе, возможно ли это?
Я пробовал это, но это не сработало, как я ожидал. Когда я создал экземпляр класса 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() вызовет ошибку времени компиляции.