На этапе разработки существуют определенные переменные, которые должны быть зафиксированы в одном и том же прогоне, но, возможно, потребуется изменить их с течением времени. Например, boolean
чтобы сигнализировать режим отладки, поэтому мы делаем вещи в программе, которую обычно не делаем.
Это плохой стиль, чтобы содержать эти значения в константе, то есть final static int CONSTANT = 0
в Java? Я знаю, что константа остается неизменной во время выполнения, но должна ли она быть одинаковой на протяжении всей разработки, за исключением, конечно, незапланированных изменений?
Я искал похожие вопросы, но не нашел ничего, что точно соответствовало бы моему.
final
дает гарантию компилятора, что программа не будет изменять значение. Я бы не отказался от этого только потому, что программист может захотеть изменить значение, назначенное в исходном коде.gravity
середине игры / пробега. Они не обязательно означают,gravity
что это одинаково на каждой планете ... Тем не менее, здоровое решение - создатьgravity
константу, но извлечь ее изplanet
файла или базы данных в начале соответствующей области.Ответы:
В Java статические конечные константы могут быть скопированы компилятором в качестве их значений в код, который их использует . В результате этого, если вы выпускаете новую версию своего кода, и существует некоторая зависимость от нисходящего потока, которая использовала эту константу, константа в этом коде не будет обновлена, если код нисходящего потока не будет перекомпилирован. Это может быть проблемой, если они затем используют эту константу с кодом, который ожидает новое значение, поскольку, хотя исходный код верен, двоичный код - нет.
Это бородавка в дизайне Java, поскольку это один из очень немногих случаев (возможно, единственный случай), когда совместимость с исходным кодом и двоичная совместимость не совпадают. За исключением этого случая, вы можете поменять зависимость с новой версией, совместимой с API, без необходимости перекомпиляции пользователей этой зависимости. Очевидно, что это чрезвычайно важно, учитывая то, как обычно управляются зависимости Java.
Хуже всего то, что код будет просто молча делать неправильные вещи, а не создавать полезные ошибки. Если бы вы заменили зависимость версией с несовместимыми определениями класса или метода, вы бы получили ошибки загрузчика классов или вызова, которые, по крайней мере, дают хорошие подсказки о том, в чем заключается проблема. Если вы не изменили тип значения, эта проблема будет выглядеть как таинственное неправильное поведение во время выполнения.
Больше всего раздражает то, что современные JVM могут легко встроить все константы во время выполнения без снижения производительности (кроме необходимости загружать класс, определяющий константу, которая, вероятно, загружается в любом случае), к сожалению, семантика языка датируется со дней до JIT , И они не могут изменить язык, потому что тогда код, скомпилированный с предыдущими компиляторами, не будет корректным. Совместимость с ошибками поражает снова.
Из-за всего этого некоторые люди советуют вообще не менять статическое конечное значение. Для библиотек, которые могут распространяться широко и обновляться неизвестными способами в неизвестное время, это хорошая практика.
В вашем собственном коде, особенно в верхней части иерархии зависимостей, вам, вероятно, это сойдет с рук. Но в этих случаях подумайте, действительно ли вам нужна постоянная, чтобы быть публичной (или защищенной). Если константа предназначена только для видимости пакета, то в зависимости от ваших обстоятельств и стандартов кода разумно, чтобы весь пакет всегда был перекомпилирован сразу, а затем проблема исчезла. Если константа закрыта, у вас нет проблем, и вы можете изменить ее, когда захотите.
источник
Все в вашем исходном коде, включая
const
объявленные глобальные константы, может быть изменено в новой версии вашего программного обеспечения.Ключевые слова
const
(илиfinal
в Java) должны указывать компилятору, что эта переменная не изменится во время работы этого экземпляра программы . Больше ничего. Если вы хотите отправлять сообщения следующему сопровождающему, используйте комментарий в источнике, для этого они и предназначены.Это лучший способ общения с вашим будущим я.
источник
TaxRate
Бытиеpublic
заставляет меня нервничать. Я хотел бы знать наверняка, что это изменение затронуло только отдел продаж, а не наших поставщиков, которые взимают с нас налог. Кто знает, что произошло в базе кода с момента написания этого комментария.public const
поля должны использоваться только для вещей, которые никогда не изменятся, например Math.pi. Если вы создаете библиотеку, вещи, которые могут измениться во время разработки или с новой версией, должны бытьpublic static readonly
такими, чтобы не вызывать проблем у пользователей вашей библиотеки.Нам нужно выделить два аспекта констант:
И затем есть связанный третий тип: переменные, значение которых не изменяется, то есть имена для значения. Разница между этими неизменяемыми переменными и константой заключается в том, что значение определяется / присваивается / инициализируется: переменная инициализируется во время выполнения, но значение константы известно во время разработки. Это различие немного мутное, так как значение может быть известно во время разработки, но фактически создается только во время инициализации.
Но если значение константы известно во время компиляции, то компилятор может выполнять вычисления с этим значением. Например, язык Java имеет концепцию константных выражений . Константное выражение - это любое выражение, состоящее только из литералов примитивов или строк, операций с константными выражениями (таких как приведение, сложение, конкатенация строк) и константных переменных. [ JLS §15.28 ] Постоянная переменная - это
final
переменная, которая инициализируется постоянным выражением. [JLS §4.12.4] Итак, для Java это константа времени компиляции:Это становится интересным, когда постоянная переменная используется в нескольких единицах компиляции, а затем объявление изменяется. Рассмотреть возможность:
A.java
:B.java
:Теперь, когда мы скомпилируем эти файлы,
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/"
хотя, возможно, некоторые или все из них должны быть указаны в файлах конфигурации, а не в коде.источник
Постоянная гарантированно будет постоянной только в течение срока службы приложения . Пока это правда, нет никаких причин не использовать преимущества языковой функции. Вам просто нужно знать, каковы последствия использования констант против флагов компилятора для той же цели:
Поддерживая приложение, которое включало бы рисование ограничивающих рамок для фигур, чтобы мы могли отладить, как они были нарисованы, мы столкнулись с проблемой. После рефакторинга весь код, который был отключен флагами компилятора, не будет компилироваться. После этого мы намеренно изменили флаги нашего компилятора на константы приложения.
Я говорю это, чтобы продемонстрировать, что есть компромиссы. Вес нескольких логических выражений не заставит приложение исчерпать память или занять слишком много места. Это может быть не так, если ваша константа действительно большой объект, который по сути имеет дескриптор всего в вашем коде. Если он не позаботится об удалении всех ссылок, которые он содержит на объект, после того, как он больше не нужен, тогда ваш объект может быть источником утечки памяти.
Я не фанат простых общих заявлений, но в целом ваш старший коллега прав. Если что-то часто меняется, возможно, это должен быть настраиваемый элемент. Например, вы можете убедить своего коллегу в константе,
IsInDebugMode = true
если вы хотите защитить некоторый код от взлома. Однако некоторые вещи, возможно, придется менять чаще, чем вы выпускаете приложение. Если это так, вам нужен способ изменить это значение в соответствующее время. Вы можете взять примерTaxRate = .065
. Это может быть правдой во время компиляции кода, но из-за новых законов он может измениться до того, как вы выпустите следующую версию своего приложения. Это то, что должно быть обновлено с помощью какого-либо механизма хранения (например, файла или базы данных)источник
#ifdef
s? Поскольку они основаны на текстовой подстановке исходного кода, они не являются частью семантики языка программирования. Обратите внимание, что в Java нет препроцессора.#ifdef
флаги. Хотя они не являются частью семантики C, они являются частью C #. Я писал для более широкого контекста языкового агностицизма.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
(кроме того, что деньги и double вредны и должны использовать BigDecimal, но тогда это не примитив и, следовательно, не ограничен), это проблема, и ее следует внимательно изучить, где она используется.источник
is a problem and should be examined with great care for where it is used.
Почему это проблема?Как следует из названия, константы не должны изменяться во время выполнения, и, по моему мнению, константы определены так, чтобы не изменяться в течение длительного времени (вы можете посмотреть на этот вопрос SO для получения дополнительной информации.
Когда возникает необходимость в флагах (например, для режима разработки), вместо этого вы должны использовать файл конфигурации или параметр запуска (многие IDE поддерживают настройку параметра запуска на основе проекта; см. Соответствующую документацию), чтобы включить этот режим - таким образом вы сохраняете гибкость в использовании такого режима, и вы не можете забыть изменять его каждый раз, когда код становится продуктивным.
источник
Возможность изменения между запусками является одним из наиболее важных моментов определения константы в вашем исходном коде!
Константа дает вам четко определенное и задокументированное (в некотором смысле) место для изменения значения всякий раз, когда вам нужно в течение жизненного цикла вашего исходного кода. Это также обещание, что изменение константы в этом месте на самом деле изменит все происшествия.
В качестве негативного примера: она не имеет смысла иметь константу ,
TRUE
которая расценивается какtrue
на языке , который на самом деле имеетtrue
ключевое слово. Вы никогда, никогда, ни разу не объявите,TRUE=false
кроме как жестокая шутка.Конечно, есть и другие способы использования констант, например, сокращение кода (
CO_NAME = 'My Great World Unique ACME Company'
), избегание дублирования (PI=3.141
), установка соглашений (TRUE=1
) или чего-то еще, но наличие определенной позиции для изменения константы, безусловно, является одним из наиболее заметных.источник