Поведение финального статического метода

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

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

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

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

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

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

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


person Harish    schedule 16.11.2009    source источник
comment
это кажется странным, +1 за вопрос, но до сих пор ни один из ответов не является удовлетворительным.   -  person Rakesh Juyal    schedule 16.11.2009
comment
Это не переопределено. Он все еще находится в A.ts().   -  person Alex Feinman    schedule 16.11.2009


Ответы (7)


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

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

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

person NawaMan    schedule 16.11.2009
comment
Чтобы, возможно, закончить ваш ответ, который я считаю правильным, проблема здесь в основном в плохом сообщении об ошибке компилятора: он должен говорить, что B не может hide ts() в A. Объявление статического метода final объявляет его нельзя скрыть. - person Sean Owen; 16.11.2009
comment
@Шон Оуэн: я тоже так думаю. Термин «скрыть» даже используется в спецификации Java, так почему бы не использовать его в сообщении компилятора. - person NawaMan; 16.11.2009
comment
Почему это вообще функция? В каком контексте это было бы полезно? - person user253751; 25.03.2015
comment
общедоступный класс Test { final static public void main (String... srik) { System.out.println (в основном методе); ц(); } public static void ts() { Child c=new Child(); c.ts(); System.out.println(Тест ТС); } } открытый класс Child extends Test { public static void ts() { System.out.println(Child ts); } } Привет. Пожалуйста, объясните мне, что происходит в этом сценарии. - person srikanth r; 20.02.2016
comment
@SeanOwen Я тоже не думаю, что это правильно, компилятор должен сказать, что, поскольку A#ts наследуется и такой метод уже существует в B, простое наличие двух методов с одинаковой сигнатурой и другим модификатором (final) не будет работать как перегрузки... Хотел бы я придумать для этого простое сообщение, хотя - person Eugene; 10.04.2018
comment
В моей книге Java How to Program Edition 10 говорится, что статические методы неявно являются окончательными (глава 10.7). prntscr.com/u3too3 Если статические методы являются окончательными и если окончательные методы нельзя скрыть. Это означает, что статические методы также не должны быть скрыты. Но мы умеем скрывать статические методы. - person Michael; 22.08.2020

статические методы нельзя переопределить

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

person Vincent Ramdhanie    schedule 16.11.2009
comment
Это правда, только не точно. статические методы нельзя переопределить, но их можно скрыть, если вы вызываете их по ссылке на экземпляр, а не по имени класса. - person John Mercier; 26.02.2015
comment
К сожалению, ваша ссылка больше не работает. Можно ли это исправить? - person Mathias Bader; 12.09.2017
comment
Ссылка на javaranch не работает, но поиск ключевых слов в Google обнаружил эту ссылку на код ранчо - person Sundeep; 28.11.2018
comment
Я отредактировал сообщение, заменив неработающую ссылку ссылкой, опубликованной Sundeep. - person MC Emperor; 17.05.2019

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

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

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

Вы могли бы подумать, что в сообщении об ошибке будет использоваться слово hidden вместо overridden...

person Powerlord    schedule 16.11.2009

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

Наличие следующих классов:

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, например, покажет вам предупреждение, если вы вызовете статический метод для экземпляра, а также если вы сделаете статический метод окончательным.

person Mathias Bader    schedule 12.09.2017

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

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

person amischiefr    schedule 16.11.2009
comment
Я не думаю, что это объясняет, почему «final» внезапно означает, что эти методы не могут сосуществовать. Как вы говорите, без «финала» нет проблем. Вы говорите, что это не переопределение, но затем говорите, что проблема в том, что B не может переопределить метод A. - person Sean Owen; 16.11.2009
comment
Конечно, да, я утверждаю, что B не видит метод ts() в A (он «скрыт»), но модификатор final не «скрывает» методы из классов, которые расширяют другой. Но э, ок. - person amischiefr; 16.11.2009
comment
Я думаю, что, возможно, ваша формулировка не должна быть переопределена в B, ее можно изменить на не следует скрывать в B. - person Scratte; 17.09.2020

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

person BalusC    schedule 16.11.2009
comment
Итак, вы считаете, что статический метод в B переопределяет метод в A? - person Koray Tugay; 24.06.2017
comment
@KorayTugay Мне просто интересно, сначала ли компилятор смотрит на потенциально переопределяемый метод (на мгновение игнорирует static), видит ли final при сбое. Просто дикая догадка, хотя - person Eugene; 30.04.2018
comment
Balus Я думаю, что это единственный ответ низкого качества, который у вас есть в StackOverflow. Учитывая все ваши исключительные ответы, этот к ним не относится. @BalusC - person Koray Tugay; 30.04.2018
comment
@KorayTugay: в то время у меня не было достаточно репутации, чтобы комментировать :) Если вы ответите, я удалю ответ, без проблем. - person BalusC; 13.05.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 ограничивает создание метода с тем же именем в дочернем классе.

person Pratik Gupta    schedule 26.11.2018
comment
Но они наследуются как статические и нестатические элементы данных. Не думаю. Они доступны из дочернего класса, но это не то же самое, что унаследовано. Ключевое слово final ограничивает создание метода с таким же именем в дочернем классе. тоже не правильно. Вы можете перегрузить статический конечный метод в дочернем классе. Вы просто не можете создать метод с той же сигнатурой. - person Scratte; 17.09.2020