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

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

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

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