Метод Java `final`: що він обіцяє?


141

У класі Java метод може бути визначений як такий final, щоб відзначити, що цей метод не може бути замінений:

public class Thingy {
    public Thingy() { ... }
    public int operationA() {...}
    /** this method does @return That and is final. */
    public final int getThat() { ...}
}

Це зрозуміло, і це може бути корисно для захисту від випадкового переосмислення або, можливо, продуктивності - але це не моє питання.

Моє запитання: З точки зору ООП, я зрозумів, що, визначаючи метод, finalдизайнер класів обіцяє, що цей метод завжди працюватиме так, як описано чи мається на увазі. Але часто це може бути поза впливом автора класу, якщо те, що робиться методом, є складнішим, то просто надання властивості .

Синтаксичне обмеження для мене зрозуміле, але що таке значення в сенсі ООП? Чи finalправильно в цьому сенсі використовується більшість авторів класу?

Який «контракт» finalобіцяє метод?

Відповіді:


156

Як вже було сказано, finalвикористовується з методом Java, щоб відзначити, що метод не може бути замінений (для об'єкта) або прихований (для статичного). Це дозволяє оригінальному розробнику створити функціональність, яку неможливо змінити підкласами, і це вся гарантія, яку він надає.

Це означає, що якщо метод спирається на інші настроювані компоненти, такі як непублічні поля / методи, функціональність остаточного методу все ще може бути налаштована. Це добре, хоча (при поліморфізмі) дозволяє частково налаштувати.

Існує низка причин, щоб запобігти налаштуванню чогось, зокрема:

  • Продуктивність - Деякі компілятори можуть аналізувати та оптимізувати операцію, особливо таку, яка не має побічних ефектів.

  • Отримайте інкапсульовані дані - подивіться незмінні об’єкти, де їх атрибути встановлюються під час побудови і ніколи не повинні змінюватися. Або обчислене значення, отримане з цих атрибутів. Хороший приклад - Stringклас Java .

  • Не всі операції, застосовні до цих компонентів, повинні бути застосовними або навіть логічними, коли вони використовуються у більшому Об'єкті. Для цього можна використовувати методи з модифікатором. Клас Counter - хороший приклад.intchardoublefinal


public class Counter {
    private int counter = 0;

    public final int count() {
        return counter++;
    }

    public final int reset() {
        return (counter = 0);
    }
}

Якщо public final int count()методу немає final, ми можемо зробити щось подібне:

Counter c = new Counter() {   
    public int count() {
        super.count();   
        return super.count();   
    } 
}

c.count(); // now count 2

Або щось подібне:

Counter c = new Counter() {
    public int count() {
        int lastCount = 0;
        for (int i = super.count(); --i >= 0; ) {
            lastCount = super.count();
        }

        return lastCount;
    }
}

c.count(); // Now double count

27

Який «контракт» обіцяє остаточний метод?

Подивіться на це інакше, будь-який не остаточний метод дає неявну гарантію, що ви можете перекрити його власною реалізацією, і клас все одно буде працювати, як очікувалося. Якщо ви не можете гарантувати, що ваш клас підтримує перезапис методу, ви повинні зробити його остаточним.


Але чи не означає це погляд на моє первісне запитання "цей остаточний метод завжди буде вести себе так, як обіцяв", що я не повинен називати жодних нефінальних методів всередині остаточного методу? Тому що, якщо я виконую покликаний метод, можливо, його було відмінено, і тому я не можу гарантувати поведінку свого остаточного методу?
букси

8

Перш за все, ви можете позначити не абстрактні класи final, а також поля та методи. Таким чином, весь клас не можна підкласифікувати. Отже, поведінка класу буде виправлена.

Я погоджуюся, що методи маркування finalне гарантують, що їх поведінка буде однаковою у підкласах, якщо ці методи викликають не завершальні методи. Якщо поведінку справді потрібно виправити, це має бути досягнуто конвенцією та ретельним проектуванням. І не забудьте це помітити в javadoc! (Документація на java)

І останнє, але не менш важливе, finalключове слово відіграє дуже важливу роль у моделі пам'яті Java (JMM). JMM гарантує, що для досягнення видимості finalполів вам не потрібна належна синхронізація. Наприклад:

class A implements Runnable {
  final String caption = "Some caption";                           

  void run() {
    // no need to synchronize here to see proper value of final field..
    System.out.println(caption);
  }
}  

так, я знаю про заключні заняття - простий випадок. Хороший пункт про заключні поля з JMM, синхронізація не потрібна ... хм: це стосується лише "покажчика", правда? Я все ще можу змінити несинхронізований об'єкт, на який він посилається (добре, не в String, а в визначених користувачем класах). Але те, що ви сказали про "остаточне, не гарантує поведінку" - це саме моя думка. Я згоден, документ і дизайн важливі.
букси

@towi ви праві, що finalне забезпечить видимість змін, внесених до складних об'єктів, таких як Map.
Віктор Сорокін

0

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


1
Так, це я мав на увазі. Правильно. Мені подобається термін "слабка гарантія" :-) І мені (в основному) подобається C ++ const. Як у char const * const = "Hello"або char const * const addName(char const * const name) const...
towi

0

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

http://download.oracle.com/javase/tutorial/java/IandI/final.html

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


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