Чи може значення константи змінюватися з часом?


28

Під час фази розробки існують певні змінні, які потрібно виправити одним і тим же циклом, але вони можуть потребувати змін у часі. Наприклад, booleanрежим налагодження сигналу, тому ми робимо те, що в програмі, як правило, не було.

Чи поганий стиль містити ці значення в постійній, тобто final static int CONSTANT = 0в Java? Я знаю, що константа залишається однаковою протягом часу запуску, але чи вона також повинна бути однаковою протягом усієї розробки, за винятком, звичайно, непередбачених змін?

Я шукав подібні запитання, але не знайшов нічого, що б точно відповідало моєму.


11
Мені цікаво, чому ти вважаєш, що це змінити погано?
Вінсент Савард

36
Якщо ви не моделюєте фізичні властивості з константами, які мають відомі математичні значення, все може змінитися за певний час.
Берин Лорич

19
програмне забезпечення м'яке .
Ерік Ейдт

10
@GregT Я б не погодився. finalдає гарантію компілятора, що програма не змінює значення. Я б не обходився з цим лише тому, що програміст може захотіти змінити значення, призначене у вихідному коді.
Олександр - Відновіть Моніку

10
Не так багато часу, щоб сформулювати повну відповідь, але я підозрюю, що ваші колеги турбуються не стільки з константами, скільки з вбудовуванням коду значень конфігурації, які можуть проявлятися як константи. ... Константи захищають вас від нерозумних помилок, як-от випадкове призначення в gravityсередині гри / бігу. Вони не обов'язково означають, що gravityце однаково на кожній планеті ... Однак, здоровим рішенням є gravityконстанта, але витягнути її з planetфайлу чи бази даних на початку відповідних областей.
svidgen

Відповіді:


6

У Java статичні кінцеві константи компілятором можуть бути скопійовані як їх значення у код, який їх використовує . У результаті цього, якщо ви випустите нову версію свого коду, і є деяка залежність нижче за течією, яка використовувала константу, константа в цьому коді не буде оновлюватися, якщо не буде перекомпільований код нижче. Це може бути проблемою, якщо вони потім використовують ту константу з кодом, який очікує нового значення, так як, навіть якщо вихідний код правильний, двійковий код не є.

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

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

Більше дратує те, що сьогоднішні JVM можуть легко вбудовувати всі константи під час виконання без штрафних санкцій (крім необхідності завантаження класу, що визначає константу, яка, ймовірно, завантажується так чи інакше), на жаль, семантика мови датується за дні до JIT . І вони не можуть змінити мову, оскільки тоді код, скомпільований з попередніх компіляторів, не буде правильним. Помилка сумісності знову вражає.

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

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


85

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

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

// DO NOT CHANGE without consulting with the legal department!
// Get written consent form from them before release!
public const int LegalLimitInSeconds = ...

Це кращий спосіб спілкуватися зі своїм майбутнім "я".


11
Мені дуже сподобався цей коментар майбутньому самому.
GregT

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

3
@IllusiveBrian не критикував використання констант. Був попереджений проти довіри коментаря, щоб бути актуальним. Завжди будьте впевнені в тому, як щось використовується, перш ніж змінити його.
candied_orange

8
Це хороша порада для Java . В інших мовах воно може бути різним. Наприклад, через те, як значення const прив'язані до сайту виклику в C #, public constполя повинні використовуватися лише для речей, які ніколи не змінюватимуться, як-от Math.pi. Якщо ви створюєте бібліотеку, те, що може змінитися під час розробки або з новою версією, має бути public static readonlyтаким, щоб не створювати проблем з користувачами вашої бібліотеки.
GrandOpener

6
Ви повинні вибрати інший приклад ... грошові цінності ніколи не повинні бути плаваючою точкою!
corsiKa

13

Нам потрібно розрізнити два аспекти констант:

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

І тут є пов'язаний третій вид: змінні, значення яких не змінюється, тобто імена для значення. Різниця між цими незмінними змінними та константою полягає в тому, коли значення визначається / призначається / ініціалізується: змінна ініціалізується під час виконання, але значення константи відоме під час розробки. Ця відмінність трохи каламутна, оскільки значення може бути відоме під час розробки, але насправді створюється лише під час ініціалізації.

Але якщо значення константи відоме під час компіляції, тоді компілятор може виконувати обчислення з цим значенням. Наприклад, мова Java має поняття постійних виразів . Постійний вираз - це будь-який вираз, який складається тільки з букварів примітивів або рядків, операцій над постійними виразами (такими як кастинг, додавання, конкатенація рядків) та постійних змінних. [ JLS §15.28 ] Постійна змінна - це finalзмінна, яка ініціалізується постійним виразом. [JLS §4.12.4] Отже, для Java це константа часу компіляції:

public static final int X = 7;

Це стає цікавим, коли константна змінна використовується у кількох одиницях компіляції, а потім декларація змінюється. Поміркуйте:

  • A.java:

    public class A { public static final int X = 7; }
  • B.java:

    public class B { public static final int Y = A.X + 2; }

Тепер, коли ми компілюємо ці файли, B.classбайт-код оголосить поле, Y = 9оскільки B.Yє постійною змінною.

Але коли ми зміняємо A.Xзмінну на інше значення (скажімо, X = 0) і перекомпілюємо лише A.javaфайл, то B.Yвсе одно посилається на старе значення. Цей стан A.X = 0, B.Y = 9не відповідає заявам у вихідному коді. Щасливого налагодження!

Це не означає, що константи ніколи не слід міняти. Константи, безумовно, кращі, ніж магічні числа, які з’являються без пояснень у вихідному коді. Тим НЕ менше, значення громадських констант є частиною вашого громадського API . Це не характерно для Java, але також зустрічається в C ++ та інших мовах, які мають окремі одиниці компіляції. Якщо ви зміните ці значення, вам потрібно буде перекомпілювати весь залежний код, тобто виконати чисту компіляцію.

Залежно від природи констант, вони могли призвести до неправильних припущень розробників. Якщо ці значення будуть змінені, вони можуть викликати помилку. Наприклад, набір констант може бути обраний таким чином, щоб вони утворювали певні бітові шаблони, наприклад public static final int R = 4, W = 2, X = 1. Якщо вони змінені, щоб утворити іншу структуру, як, наприклад, R = 0, W = 1, X = 2існуючий код, наприклад, boolean canRead = perms & Rстає неправильним. І просто подумайте про те, що слід було Integer.MAX_VALUEб змінити! Тут немає жодних виправлень, важливо пам’ятати, що значення деяких констант дійсно важливо і їх неможливо змінити просто.

Але для більшості констант їх зміна буде нормальною, доки враховуються вищезазначені обмеження. Константа безпечно змінювати, коли важливо значення, а не конкретне значення. Наприклад, це стосується таких змін, як BORDER_WIDTH = 2або TIMEOUT = 60; // secondsабо шаблони, такі як, API_ENDPOINT = "https://api.example.com/v2/"хоча, можливо, деякі або всі з них повинні бути вказані у файлах конфігурації, а не в коді.


5
Мені подобається цей аналіз. Я читаю це як: Ви можете змінювати константу до тих пір, поки не зрозумієте, як це використовується.
candied_orange

+1 C # також "страждає" від того ж питання з громадськими константами.
Регінальд Блю

6

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

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

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

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

Вам потрібно оцінити випадок використання і чому ви хочете змінити константи.

Я не прихильник простих бланкетних заяв, але загалом ваш старший колега є правильним. Якщо щось неодмінно змінюється, можливо, це може бути налаштованим елементом. Наприклад, ви могли, напевно, переконати свого колегу в постійному, IsInDebugMode = trueколи хочете захистити якийсь код від його зламу. Однак деякі речі можуть знадобитися змінюватись частіше, ніж ви випускаєте програму. Якщо це так, вам потрібен спосіб змінити це значення у відповідний час. Ви можете взяти за приклад а TaxRate = .065. Це може бути правдою під час компіляції коду, але через нові закони він може змінитися до випуску наступної версії програми. Це щось, що потребує оновлення або через якийсь механізм зберігання (наприклад, файл чи базу даних)


Що ви розумієте під "прапорами компілятора"? Можливо, препроцесор C та подібні функції компілятора, які підтримують макроси / визначення та #ifdefs? Оскільки вони засновані на текстовій заміні вихідного коду, вони не є частиною семантики мови програмування. Зауважте, що у Java немає препроцесора.
амон

@amon, Java може не, але є кілька мов. Я маю на увазі #ifdefпрапори. Хоча вони не є частиною семантики С, вони є частиною C #. Я писав для більш широкого контексту мовного агностицизму.
Берин Лорич

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

@ Олександр, я згоден. Це те, про що слід пам’ятати.
Берін Лорич

1
"Константи займають простір додатків" - якщо ви не розробляєте вбудовану програму для мікроконтролера, що має лише кілобайт або два пам'яті, ви навіть не повинні думати про такі речі.
vsz

2

const, #defineАбо finalце компілятор натяк (зверніть увагу , що #defineце на самому ділі не натяк, його макро і значно потужніший). Це вказує, що значення не змінюватиметься під час виконання програми, і можуть бути зроблені різні оптимізації.

Однак, як натяк компілятора, компілятор робить те, чого програміст може не завжди очікувати. Зокрема, javac вбудує вбудований ряд static final int FOO = 42;так, що в будь-якому місці, де FOOвикористовується, буде прочитаний фактично складений байт-код 42.

Це не є великим сюрпризом, поки хтось не змінить значення, не перекомпілювавши інший блок компіляції (.java-файл) - і 42залишиться в байтовому коді (див., Чи можна відключити вбудовування javac статичних кінцевих змінних? ).

Зробити щось static finalозначає, що це те, що буде назавжди більше, і змінити це - дійсно велика угода - особливо, якщо це щось інше private.

Константи для таких речей final static int ZERO = 0не є проблемою. final static double TAX_RATE = 0.55(окрім того, що гроші та подвійні - це погано, і слід використовувати BigDecimal, але тоді це не примітивне і, таким чином, не накреслене) є проблемою, і слід з великою уважністю вивчати те, де він використовується.


для малих значень ZERO.

3
is a problem and should be examined with great care for where it is used.Чому це проблема?
Олександр - Відновіть Моніку

1

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

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


0

Можливість змінити прогони є одним з найважливіших моментів визначення константи у вихідному коді!

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

В якості негативного прикладу: вона НЕ має сенсу мати константу , TRUEяка розцінюється як trueна мові , який на самому справі має trueключове слово. Ви ніколи, ніколи, ні разу не заявляли, TRUE=falseхіба що як жорстокий жарт.

Звичайно, є й інші способи використання констант, наприклад, скорочення коду ( CO_NAME = 'My Great World Unique ACME Company'), уникнення дублювання ( PI=3.141), встановлення умов ( TRUE=1) або будь-якого іншого, але певне положення для зміни константи, безумовно, одне з найвизначніших.

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