Поведение конечного статического метода
Я играл с модификаторами со статическим методом и сталкивался с странным поведением.
Как мы знаем, статические методы нельзя переопределить, поскольку они связаны с классом, а не с экземпляром.
Итак, если у меня есть следующий сниппет, он компилирует fine
//Snippet 1 - Compiles fine
public class A {
static void ts() {
}
}
class B extends A {
static void ts() {
}
}
Но если я включу окончательный модификатор в статический метод в классе A, то компиляция завершится неудачей
ts() в B не может переопределить ts() в A; переопределенный метод является статическим окончательным.
Почему это происходит, когда статический метод не может быть переопределен вообще?
Ответы
Ответ 1
Статические методы нельзя переопределить, но они могут быть скрыты. Метод ts()
B не является переопределяющим (не подверженным полиморфизму) ts()
оператора A, но он скроет его. Если вы вызываете ts()
в B (NOT A.ts()
или B.ts()
... just ts()
), то вызов из B будет вызываться, а не A. Поскольку это не подвержено полиморфизму, вызов ts()
в никогда не будет перенаправлен на объект в B.
Ключевое слово final
отключит метод от скрытия. Поэтому они не могут быть скрыты, и попытка сделать это приведет к ошибке компилятора.
Надеюсь, что это поможет.
Ответ 2
статические методы не могут быть переопределены
Это не совсем так. Пример кода действительно означает, что метод ts в B скрывает метод ts в A. Так что его переопределение не совсем точно. На Джаваранче есть хорошее объяснение.
Ответ 3
Статические методы относятся к классу, а не к экземпляру.
A.ts()
и B.ts()
всегда будут отдельными методами.
Реальная проблема заключается в том, что Java позволяет вызывать статические методы для объекта экземпляра. Статические методы с одной и той же сигнатурой из родительского класса hidden при вызове из экземпляра подкласса. Однако вы не можете переопределить/скрыть окончательные методы.
Вы думаете, что сообщение об ошибке будет использовать слово hidden вместо переопределенного...
Ответ 4
Возможно, вы можете подумать о том, чтобы сделать статический метод окончательным, учитывая следующее:
Имея следующие классы:
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 покажет вам предупреждение, если вы вызовете статический метод для экземпляра, а также, если вы сделаете статический метод окончательным.
Ответ 5
Я думаю, что ошибка компиляции здесь довольно вводила в заблуждение. Он не должен был говорить, что "переопределенный метод является статическим окончательным". Но вместо этого он должен сказать, что "переопределенный метод является окончательным". Статический модификатор здесь не имеет значения.
Ответ 6
Метод ts() в B не переопределяет метод ts() в A, это просто другой метод. Класс B не видит метод ts() в A, поскольку он является статическим, поэтому он может объявить свой собственный метод ts().
Однако, если этот метод является окончательным, компилятор подберет, что в есть метод ts(), который не должен быть переопределен в B.
Ответ 7
Статический метод не может быть переопределен в 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 ограничивает создание метода с тем же именем в дочернем классе.