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


123

Я грав із модифікаторами статичним методом і натрапив на дивну поведінку.

Як ми знаємо, статичні методи не можна перекрити, оскільки вони асоціюються з класом, а не інстанцією.

Отже, якщо у мене є фрагмент нижче, він складається добре

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

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

Але якщо я включу остаточний модифікатор до статичного методу в класі A, то компіляція не вдається ts () в B не може замінити ts () в A; метод, що перекрито, є статичним остаточним .

Чому це трапляється, коли статичний метод взагалі не можна змінити?


2
здається дивним, +1 для питання, але до цих пір жодна з відповідей не є задовільною.
Ракеш Джуял

1
Це не перекрито. Це все ще є в A.ts ().
Алекс Фейнман

Відповіді:


166

Статичні методи не можна перекрити, але їх можна приховати. ts()Метод B не перекриваючи (не підлягає поліморфізм) ts()А , але вона буде приховати. Якщо ви зателефонуєте ts()в B (НЕ A.ts()або B.ts()... просто ts()), той з B буде називатися, а не А. Оскільки це не піддається поліморфізму, виклик ts()в A ніколи не буде перенаправлений на той, який знаходиться в B.

Ключове слово finalвідключить метод не приховувати. Тому їх не можна приховати, і спроба зробити це призведе до помилки компілятора.

Сподіваюся, це допомагає.


30
Можливо, я закінчую свою відповідь, і це правильно, я вважаю, що проблема в основному є поганим повідомленням про помилку компілятора: воно повинно сказати, що B не може приховати ts () у A. Оголошення статичного методу остаточним - це оголошення, яке воно не може бути приховане.
Шон Оуен

2
@Sean Owen: Я теж думаю. Термін "приховати" використовується навіть у специфікації Java, тому чому б не використовувати його у повідомленні компілятора.
NawaMan

2
Чому це навіть особливість? У якому контексті це було б корисно?
користувач253751

Тест публічного класу {final static public void main (String ... srik) {System.out.println ("In main method"); ц (); } публічна статична пустота ts () {Child c = new Child (); c.ts (); System.out.println ("Тест ц"); }} публічний клас Child розширює тест {public static void ts () {System.out.println ("Дочірнє ц"); }} Привіт Ви можете пояснити мені, що відбувається в цьому сценарії
srikanth r

@SeanOwen Я також не вважаю, що це правильно, компілятор повинен сказати, що оскільки A#tsвін успадковується і такий метод вже існує у B, просто маючи два методи з однаковою підписом та іншим модифікатором ( final), він не працюватиме як перевантаження .. Хотілося б, щоб я міг придумати просте повідомлення для цього
Євген

13

статичні методи не можна перекрити

Це не зовсім так. Приклад код дійсно означає, що метод ts в B приховує метод ts в A. Отже, його не зовсім переважає. На Javaranch є приємне пояснення.


4
Це правда просто не точно. статичні методи не можуть бути замінені, але їх можна приховати, якщо ви викликаєте їх на посилання на екземпляр, а не на ім'я класу.
Джон Мерсьє,

1
На жаль, ваше посилання більше не працює. Чи можна це виправити?
Mathias Bader

Посилання javaranch не працює, але Googling з ключовими словами знайшов це посилання на ранчо з кодом
Sundeep

Я відредагував публікацію, замінивши мертве посилання на посилання, розміщене Sundeep.
MC Імператор

10

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

A.ts()і B.ts()завжди будуть окремими методами.

Справжня проблема полягає в тому, що Java дозволяє викликати статичні методи на об’єкті екземпляра. Статичні методи з однаковим підписом від батьківського класу приховані, коли викликаються з екземпляра підкласу. Однак ви не можете змінити / приховати остаточні методи .

Ви можете подумати, що повідомлення про помилку буде використовувати слово приховане замість перекресленого ...


6

Можливо, ви можете подумати про те, щоб статичний метод остаточним, враховуючи наступне:

Проводячи наступні класи:

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


0

Метод ts () в B не переважає метод ts () в A, це просто інший метод. Клас B не бачить метод ts () в A, оскільки він є статичним, тому він може оголосити власний метод під назвою ts ().

Однак якщо метод є остаточним, тоді компілятор визначить, що в A є метод ts (), який не слід перекривати в B.


Я не думаю, що це пояснює, чому "остаточний" раптом означає, що ці методи не можуть співіснувати. Як ви кажете, без "остаточного" немає жодних проблем. Ви кажете, що це не перевищує значення, але потім говорите, що проблема полягає в тому, що B не може змінити метод A.
Шон Оуен

Звичайно, я заявляю, що B не бачить методу ts () в A (він "прихований"), але остаточний модифікатор не "приховує" методи від класів, що розширюють інший. Але так, добре.
amischiefr

0

Я думаю, що помилка компіляції тут була досить оманливою. Він не повинен був говорити, що "переосмислений метод є статичним остаточним", але він повинен був сказати "метод, що переосмислюється, є остаточним". Статичний модифікатор тут не має значення.


1
Отже, ви розглядаєте статичний метод у B, який переважає той, що знаходиться в A?
Корай Тугай

@KorayTugay Мені просто цікаво, чи компілятор спочатку дивиться на потенційно перезавантажений метод (ігнорує статичне на мить), бачить остаточне на провал. Просто дика здогадка
Євген

Балус, я думаю, що це єдина відповідь низької якості, яку ти маєш у StackOverflow. Враховуючи всі ваші виняткові відповіді, ця не належить до них. @BalusC
Корай Тугай

@KorayTugay: на той момент у мене не було достатньої репутації для коментарів :) Якщо ви надішлете повідомлення, я видалю відповідь, без проблем.
BalusC

0

Статичний метод не може бути замінений на 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ключових словах гарантує , що метод конкретного орган буде запускати кожен раз, коли виклик методу. Тепер, якщо статичний метод створений у дочірньому класі з тим самим іменем і здійснюється виклик методу, метод у підкласі виконується, що не повинно бути випадком, якщо остаточний префікс перед іменем статичного методу в батьківському класі . Отже, заключне ключове слово обмежує створення методу з тим самим іменем у дочірньому класі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.