Можно ли переопределить статический метод в производном классе? - PullRequest
11 голосов
/ 14 мая 2010

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

Я попробовал это, но это не сработало, как я ожидал. Когда я создал экземпляр класса 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");
  }
}

Ответы [ 7 ]

26 голосов
/ 07 февраля 2013

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 () является (барабанная дробь, пожалуйста ...) методом экземпляра, в котором Bar переопределяет метод из Foo, во время выполнения JVM использует фактический класс экземпляра f, чтобы определить, какой метод запустить. Хотя f был объявлен как Foo, фактический экземпляр, который мы создали, был новым Bar (). Поэтому во время выполнения JVM обнаруживает, что f является экземпляром Bar, и поэтому она вызывает instanceMethod () в Bar, а не в Foo. Вот как обычно работает Java для методов экземпляра.

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

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

18 голосов
/ 14 мая 2010

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

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
4 голосов
/ 14 мая 2010

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

1 голос
/ 14 мая 2010

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

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

0 голосов
/ 28 марта 2014

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

0 голосов
/ 14 мая 2010

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

A.foo();

или

 A a = new A();
 a.foo();
0 голосов
/ 14 мая 2010

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

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

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