Поведение окончательного статического метода - PullRequest
114 голосов
/ 16 ноября 2009

Я играл с модификаторами статическим методом и столкнулся со странным поведением.

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

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

//Snippet 1 - Compiles fine
public class A {
    static void ts() {
    }
}

class B extends A {
    static void ts() {
    }
}

Но если я добавлю окончательный модификатор к статическому методу в классе A, то компиляция завершится неудачно ts () в B не может переопределить ts () в A; переопределенный метод является статическим final .

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

Ответы [ 7 ]

153 голосов
/ 16 ноября 2009

Статические методы не могут быть переопределены, но они могут быть скрыты. Метод ts() для B не переопределяет (не подвержен полиморфизму) ts() для A, но скрывает его. Если вы позвоните ts() в B (НЕ A.ts() или B.ts() ... просто ts()), будет вызван один из B, а не A. Поскольку это не подвергается полиморфизму, вызов ts() в A никогда не будет перенаправлен в B.

Ключевое слово final отключит метод от скрытия. Поэтому они не могут быть скрыты, и попытка сделать это приведет к ошибке компилятора.

Надеюсь, это поможет.

12 голосов
/ 16 ноября 2009

статические методы не могут быть переопределены

Это не совсем так. Пример кода действительно означает, что метод ts в B скрывает метод ts в A. Так что его переопределение не совсем точно. На Джаваранче есть хорошее объяснение.

10 голосов
/ 16 ноября 2009

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

A.ts() и B.ts() всегда будут отдельными методами.

Настоящая проблема в том, что Java позволяет вам вызывать статические методы для объекта экземпляра. Статические методы с той же сигнатурой родительского класса скрыты при вызове из экземпляра подкласса. Однако вы не можете переопределить / скрыть final методы .

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

5 голосов
/ 12 сентября 2017

Вы можете подумать о том, чтобы сделать статический метод окончательным, учитывая следующее:

Имея следующие классы:

class A {
    static void ts() {
        System.out.print("A");
    }
}
class B extends A {
    static void ts() {
        System.out.print("B");
    }
}

Теперь «правильный» способ вызова этих методов будет

A.ts();
B.ts();

, что приведет к AB, но вы также можете вызывать методы для экземпляров:

A a = new A();
a.ts();
B b = new B();
b.ts();

, что также приведет к AB.

Теперь рассмотрим следующее:

A a = new B();
a.ts();

это напечатало бы A. Это может вас удивить, поскольку у вас есть объект класса B. Но так как вы вызываете его из ссылки типа A, он будет вызывать A.ts(). Вы можете напечатать B со следующим кодом:

A a = new B();
((B)a).ts();

В обоих случаях у вас есть объект из класса B. Но в зависимости от указателя, который указывает на объект, вы будете вызывать метод с A или с B.

Теперь предположим, что вы разработчик класса A и хотите разрешить подклассы. Но вам действительно нужен метод ts(), когда бы он ни вызывался, даже из подкласса, то есть он делает то, что вы хотите, чтобы он не скрывался версией подкласса. Тогда вы можете сделать это final и предотвратить его скрытие в подклассе. И вы можете быть уверены, что следующий код вызовет метод из вашего класса A:

B b = new B();
b.ts();

Хорошо, конечно, это как-то сконструировано, но в некоторых случаях это может иметь смысл.

Вы не должны вызывать статические методы для экземпляров, а непосредственно для классов - тогда у вас не возникнет этой проблемы. Также IntelliJ IDEA, например, покажет вам предупреждение, если вы вызываете статический метод для экземпляра, а также если вы делаете статический метод финальным.

1 голос
/ 16 ноября 2009

Я думаю, что ошибка компиляции была здесь весьма обманчивой. Он не должен был сказать «переопределенный метод является статическим окончанием», но вместо этого он должен был сказать «переопределенный метод является окончательным». Модификатор статики здесь не имеет значения.

0 голосов
/ 26 ноября 2018

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

class Writer { 
    public static void doo(){
        System.out.println("sth");
    } 
}
class Author extends Writer{ 
    public void doo(){
        System.out.println("ok"); // error overridden method is static
    }
}

Ключевое слово final гарантирует, что конкретное тело метода будет запускаться при каждом вызове метода. Теперь, если в дочернем классе с тем же именем создается статический метод и выполняется вызов метода, метод в подклассе выполняется, что не должно быть, если перед префиксом final перед именем статического метода в родительском классе выполняется метод , Следовательно, ключевое слово final ограничивает создание метода с тем же именем в дочернем классе.

0 голосов
/ 16 ноября 2009

Метод ts () в B не переопределяет метод ts () в A, это просто другой метод. Класс B не видит метод ts () в A, поскольку он является статическим, поэтому он может объявить свой собственный метод с именем ts ().

Однако, если метод является окончательным, компилятор обнаружит, что в A есть метод ts (), который не должен быть переопределен в B.

...