Я видел такие примеры:
public class MaxSeconds {
public static final int MAX_SECONDS = 25;
}
и предположил, что у меня может быть класс Constants для обертывания констант, объявив их static final. Я практически не знаю Java, и мне интересно, лучший ли это способ создания констант.
Аналогично: Совместное использование постоянных строк в Java во многих классах?




Это вполне приемлемо, наверное, даже стандартно.
(public/private) static final TYPE NAME = VALUE;
где TYPE - это тип, NAME - это имя, написанное заглавными буквами с подчеркиванием вместо пробелов, а VALUE - постоянное значение;
Я настоятельно рекомендую НЕ помещать ваши константы в их собственные классы или интерфейсы.
В качестве примечания: переменные, которые объявлены окончательными и являются изменяемыми, все еще могут быть изменены; однако переменная никогда не может указывать на другой объект.
Например:
public static final Point ORIGIN = new Point(0,0);
public static void main(String[] args){
ORIGIN.x = 3;
}
Это допустимо, и тогда ORIGIN будет точкой в (3, 0).
Вы даже можете «импортировать статические MaxSeconds.MAX_SECONDS;» так что вам не нужно писать MaxSeconds.MAX_SECONDS
Предыдущий комментарий о статическом импорте относится только к Java 5+. Некоторые считают, что сокращение не стоит возможной путаницы относительно того, откуда взялась константа, при чтении длинного кода может быть легче следить за MaxSeconds.MAX_SECONDS, чем подниматься и смотреть на импорт.
Поскольку вы можете изменить objetc, как показал janguy, лучше всего, если ваши константы являются неизменяемыми объектами или просто примитивами / строками.
Если вы читаете этот вопрос, пожалуйста, прочтите следующие два ответа ниже, прежде чем относиться к этому ответу слишком серьезно. Несмотря на то, что он принят, он является спорным, если не ошибочным.
@jjnguy, вы не ошибаетесь, но если бы я прочитал только вопрос и первые пару строк вашего ответа, у меня создалось бы впечатление, что «класс констант» «вполне приемлем, возможно, даже стандарт». Который понятие является неверно.
Когда бы вы сделали свою константу, описанную выше, private? В идеале он должен быть объявлен public, чтобы другие классы могли его использовать. Нет?
@realPK Это зависит от контекста, например, вы хотите иметь один класс в качестве соединителя базы данных и создать константу для строки подключения, которая будет использоваться только внутри этого класса, и она не должна быть доступна для других классов. Это просто пример, потому что использовать строки подключения в качестве констант - плохая практика, просто чтобы показать мою точку зрения. Надеюсь это поможет.
Обратите внимание, что у многомодульных проектов могут быть проблемы с областью видимости, ведь константы должны быть помещены в общий модуль.
Волей-неволей придумывает случайные правила или рекомендации, которые не имеют жизнеспособных рифм или причин, кроме тех, которые соответствуют (плохим) личным привычкам рекомендующего.
Я бы не назвал этот класс тем же (кроме корпуса), что и константа ... Я бы имел как минимум один класс «Настройки», или «Значения», или «Константы», где бы жили все константы. Если у меня их много, я бы сгруппировал их в классы логических констант (UserSettings, AppSettings и т. д.)
Я планировал создать класс под названием Constants, это был лишь небольшой пример, который я нашел.
Это правильный путь.
Обычно константы нет хранятся в отдельных классах «Константы», потому что их нельзя обнаружить. Если константа имеет отношение к текущему классу, сохранение их там поможет следующему разработчику.
Просто избегайте использования интерфейса:
public interface MyConstants {
String CONSTANT_ONE = "foo";
}
public class NeddsConstant implements MyConstants {
}
Это заманчиво, но нарушает инкапсуляцию и стирает различия в определениях классов.
Действительно, статический импорт намного предпочтительнее.
Эта практика представляет собой странное использование интерфейсов, которые предназначены для определения услуг, предоставляемых классом.
Я не возражаю против использования интерфейса для констант, но я думаю, что ваш пример вводит в заблуждение. Вам не нужно реализовывать интерфейс, чтобы получить доступ к константе, потому что она неявно статическая, общедоступная и окончательная. Итак, это проще, чем вы описали здесь.
Это правда ... это нарушает инкапсуляцию, я предпочитаю использовать final Класс констант, которые выглядят более объектно, и предпочтительнее интерфейса ИЛИ перечисления. Примечание: Enum может быть избегать, если Android не нужен.
Чтобы сделать еще один шаг вперед, вы можете разместить глобально используемые константы в интерфейсе, чтобы их можно было использовать в масштабах всей системы. Например.
public interface MyGlobalConstants {
public static final int TIMEOUT_IN_SECS = 25;
}
Но не применяйте его потом. Просто обратитесь к ним прямо в коде через полное имя класса.
Суть объявления их в интерфейсе (а не его реализации) заключается в том, что вы можете пропустить «общедоступный статический финал».
Интерфейсы предназначены для определения поведенческого контракта, а не удобного механизма для хранения констант.
@JohnTopley Да, но это работает. ;)
А как насчет перечисления?
Я бы очень не советовал иметь один класс констант. В то время это может показаться хорошей идеей, но когда разработчики отказываются документировать константы, и класс увеличивается до более чем 500 констант, которые вообще не связаны друг с другом (связаны с совершенно разными аспектами приложения), это обычно превращается в совершенно нечитаемый файл констант. Вместо:
полностью согласен ... это можно задокументировать как классический антипаттерн для больших проектов.
Не по теме, но ... Обобщения, конечно, не идеальны, но я думаю, вы могли бы привести довольно хороший аргумент в пользу того, что они являются полезной функцией Java 5 :)
Что касается вашего мнения, это мнение, а не ответ, поэтому его следует опубликовать как комментарий в соответствии с текущими стандартами. Я знаю, что он старый, но ничего не меняет.
@ ŁukaszL. Сам вопрос на самом деле основан на мнении (всякий раз, когда возникает «лучший», это обычно вопрос мнения), поэтому ответ является правильным ответом на этот вопрос. Я дал ответ на вопрос, каким (да, я должен быть полагать, так что да, это мнение, потому что снова "лучший" меняется со временем и, как правило, основан на мнении) лучший способ реализовать константы в Java.
Ни один из вышеперечисленных вариантов не дает вам истинной глобальной переменной. Что, если у меня есть что-то, что используется везде, но не перечисление? Могу ли я перечислить только одну вещь?
Если вам нужна глобальная константа, охватывающая все модули, вероятно, что-то не так с вашей стратегией проектирования. Если вам В самом деле нужна глобальная константа, создайте для нее общедоступный конечный класс в пакете верхнего уровня и вставьте его туда. Затем удалите его, как только вы поймете, что не все ваших классов действительно нуждается в этой константе, и переместите его в пакет, который ссылается на нее больше всего. Вы можете совместно использовать константу между пакетами, но требование глобальной константы, не являющейся перечислимым типом, - это неприятный запах, поскольку перечисляемые типы могут иметь поведение, но строка - это строка, int - это int и т.
Я согласен с @markthegrea, потому что некоторые люди не добавляют комментарии и не документируют константы - плохая идея использовать класс. Это не тот случай, когда все люди, которые модифицируют константы, занимаются разработкой. Например, люди, которые работают с файлами текстур, могут просто обновить постоянное значение и могут перекомпилировать игру и проверить результат, и нет необходимости искать файлы, или кто-то, кто хочет подготовить документ для констант (например, ограничений), может обратиться к один файл вместо поиска по всем файлам.
Это ПЛОХАЯ ПРАКТИКА для использования интерфейсов только для хранения констант (названный постоянный шаблон интерфейса Джошем Блохом). Вот что советует Джош:
If the constants are strongly tied to an existing class or interface, you should add them to the class or interface. For example, all of the boxed numerical primitive classes, such as Integer and Double, export MIN_VALUE and MAX_VALUE constants. If the constants are best viewed as members of an enumerated type, you should export them with an enum type. Otherwise, you should export the constants with a noninstantiable utility class.
Пример:
// Constant utility class
package com.effectivejava.science;
public class PhysicalConstants {
private PhysicalConstants() { } // Prevents instantiation
public static final double AVOGADROS_NUMBER = 6.02214199e23;
public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double ELECTRON_MASS = 9.10938188e-31;
}
О соглашении об именах:
By convention, such fields have names consisting of capital letters, with words separated by underscores. It is critical that these fields contain either primitive values or references to immutable objects.
Если вы собираетесь назвать что-то плохим, возможно, вам стоит объяснить, почему вы так думаете?
Это старый пост, но лучше использовать нотацию abstract для класса вместо частного конструктора.
@XtremeBiker, пожалуйста, объясните; [citation-needed].
Мэтт, хотя рекомендация байкера не является ложной, я бы выступал за финальные классы, а не за абстрактные классы. Суть байкера в том, что вы хотите убедиться, что ваш класс констант не может быть изменен. Таким образом, помечая его как окончательный, вы не позволяете ему быть подклассом или экземпляром. Это также помогает инкапсулировать его статические функции и не позволяет другим разработчикам создавать его подклассы и заставлять делать то, для чего он не предназначен.
@ liltitus27: пометка класса "final" не препятствует его созданию! Возможно, вы хотели сказать, что ДОПОЛНИТЕЛЬНО наличие частного конструктора (для предотвращения создания экземпляров) класс ТАКЖЕ должен быть помечен как "final" (чтобы предотвратить его создание подклассов)? ОДНАКО нет причин для этого. Фактически, ПОЛЕЗНО иметь возможность создать подкласс этого класса констант. Например, предположим, что другой модуль желает включить эти константы и добавить свои собственные. Самый простой способ сделать это - создать подкласс. (а также добавить в подкласс частный конструктор).
@XtremeBiker Маркировка класса abstract вместо частного конструктора не полностью предотвращает создание экземпляра, потому что его можно создать подклассом и создать экземпляр подкласса (не то чтобы это хорошая идея, но это возможно). @ToolmakerSteve. Вы не можете создать подкласс класса с частным конструктором (по крайней мере, без большого плохого взлома), потому что конструктор подкласса должен вызвать (теперь частный) конструктор своего суперкласса. Таким образом, маркировка final не требуется (но, возможно, более явно).
Еще хуже использовать класс только для хранения констант. Если вы никогда не хотите создавать объект этого класса, почему вы в первую очередь используете обычный учебный класс?
Чтобы обеспечить выполнение неинстанцируемых классов, лучше всего иметь throw new AssertionError(); внутри частного конструктора, чтобы убедиться, что он не создается посредством отражения.
В Effective Java (2-е издание) рекомендуется использовать перечисления вместо статических целых чисел для констант.
Здесь есть хорошая запись о перечислениях на Java: http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html
Обратите внимание, что в конце этой статьи задается следующий вопрос:
So when should you use enums?
При ответе:
Any time you need a fixed set of constants
Я согласен с тем, что использование интерфейса - это не лучший вариант. У избегания этого шаблона даже есть свой собственный пункт (№ 18) в Эффективная Java Блоха.
Аргумент, который Блох выдвигает против шаблона константного интерфейса, состоит в том, что использование констант - это деталь реализации, но реализация интерфейса для их использования раскрывает эту деталь реализации в вашем экспортированном API.
Паттерн public|private static final TYPE NAME = VALUE; - хороший способ объявить константу. Лично я считаю, что лучше избегать создания отдельного класса для размещения всех ваших констант, но я никогда не видел причины не делать этого, кроме личных предпочтений и стиля.
Если ваши константы можно хорошо смоделировать в виде перечисления, рассмотрите структуру перечислить, доступную в версии 1.5 или новее.
Если вы используете более раннюю версию, чем 1.5, вы все равно можете выполнять типизированные перечисления, используя обычные классы Java. (Подробнее об этом см. этот сайт).
Некоторые константы могут использоваться для вызова API. Например, см. Интерфейс org.springframework.transaction.TransactionDefinition. В нем есть список констант, например int PROPAGATION_REQUIRED = 0;
Я знаю, что это устарело, но ссылка на Эффективную Java Блоха не работает. Не могли бы вы предоставить другую или другую ссылку, подтверждающую, что «использование интерфейса - не лучший вариант»?
Ошибка номер один, которую вы можете сделать, - это создать глобально доступный класс с общим именем, например Constants. Это просто засоряется мусором, и вы теряете всякую возможность выяснить, какая часть вашей системы использует эти константы.
Вместо этого константы должны входить в класс, который «владеет» ими. У вас есть константа TIMEOUT? Вероятно, он должен войти в ваш класс Communications () или Connection (). MAX_BAD_LOGINS_PER_HOUR? Переходит в User (). И так далее.
Другое возможное использование - это файлы Java .properties, когда «константы» могут быть определены во время выполнения, но не могут быть легко изменены пользователем. Вы можете упаковать их в свои .jars и ссылаться на них с помощью Class resourceLoader.
И, конечно же, вы никогда не захотите получить доступ к константе из более чем одного класса или избежать беспорядка в верхней части класса.
Для констант Enum - лучший выбор IMHO. Вот пример
public class myClass {
public enum myEnum {
Option1("String1", 2),
Option2("String2", 2)
;
String str;
int i;
myEnum(String str1, int i1) { this.str = str1 ; this.i1 = i }
}
Константу любого типа можно объявить путем создания неизменяемого свойства внутри класса (то есть переменной-члена с модификатором final). Обычно также предоставляются модификаторы static и public.
public class OfficePrinter {
public static final String STATE = "Ready";
}
Существует множество приложений, в которых значение константы указывает выбор из n-кортежа (например, перечисление) вариантов. В нашем примере мы можем выбрать определение перечислимого типа, который будет ограничивать возможные присвоенные значения (т.е. улучшенный безопасность типа):
public class OfficePrinter {
public enum PrinterState { Ready, PCLoadLetter, OutOfToner, Offline };
public static final PrinterState STATE = PrinterState.Ready;
}
Один из способов сделать это - создать «глобальный» класс с постоянными значениями и выполнить статический импорт в классы, которым нужен доступ к константе.
Единый универсальный класс констант - плохая идея. Константы должны быть сгруппированы по классу, с которым они наиболее логически связаны.
Вместо того, чтобы использовать какие-либо переменные (особенно перечисления), я бы посоветовал вам использовать методы. Создайте метод с тем же именем, что и переменная, и попросите его вернуть значение, которое вы присвоили переменной. Теперь удалите переменную и замените все ссылки на нее вызовами только что созданного метода. Если вы чувствуете, что константа достаточно универсальна, и вам не нужно создавать экземпляр класса только для его использования, тогда сделайте метод константы методом класса.
Хороший объектно-ориентированный дизайн не должен нуждаться во многих общедоступных константах. Большинство констант следует инкапсулировать в класс, которому они нужны для выполнения своей работы.
Или вводится в этот класс через конструктор.
Я предпочитаю использовать геттеры, а не константы. Эти геттеры могут возвращать постоянные значения, например public int getMaxConnections() {return 10;}, но все, что требует константы, будет проходить через геттер.
Одно из преимуществ состоит в том, что если ваша программа перерастает константу - вы обнаруживаете, что ее нужно настраивать, - вы можете просто изменить способ возврата константы геттером.
Другое преимущество состоит в том, что для изменения константы вам не нужно перекомпилировать все, что ее использует. Когда вы ссылаетесь на статическое конечное поле, значение этой константы компилируется в любой байт-код, который на него ссылается.
Хорошая перекомпиляция классов ссылок вряд ли станет бременем в 21 веке. И вы никогда не должны использовать модель аксессора / мутатора (получателя / установщика) для вещей Другой, кроме доступа и изменения переменных-членов. Константы концептуально предназначены для непосредственного использования, в то время как геттеры / сеттеры (оба) предназначены для управления государственный. Кроме того, вы просто вызываете путаницу: люди не ожидают, что геттер даст простую константу.
FWIW, значение тайм-аута в секундах, вероятно, должно быть параметром конфигурации (считанным из файла свойств или посредством инъекции, как в Spring), а не константой.
Создание статических конечных констант в отдельном классе может вызвать проблемы. Компилятор Java фактически оптимизирует это и поместит фактическое значение константы в любой класс, который на нее ссылается.
Если позже вы измените класс «Константы» и не выполните жесткую повторную компиляцию для других классов, которые ссылаются на этот класс, вы получите комбинацию старых и новых значений.
Вместо того, чтобы думать о них как о константах, подумайте о них как о параметрах конфигурации и создайте класс для управления ими. Пусть значения не являются окончательными, и даже подумайте об использовании геттеров. В будущем, когда вы решите, что некоторые из этих параметров действительно должны настраиваться пользователем или администратором, это будет намного проще сделать.
+1 за это отличное предостережение. (Если вы не можете гарантировать, что это постоянная константа, рассмотрите вместо этого геттер, как упоминает big_peanut_horse.) Кстати, то же самое относится и к const в C#: msdn.microsoft.com/en-us/library/e6w8fe1b.aspx
В чем разница
1.
public interface MyGlobalConstants {
public static final int TIMEOUT_IN_SECS = 25;
}
2.
public class MyGlobalConstants {
private MyGlobalConstants () {} // Prevents instantiation
public static final int TIMEOUT_IN_SECS = 25;
}
и используя
MyGlobalConstants.TIMEOUT_IN_SECS везде, где нам нужна эта константа. Я думаю, что оба они одинаковы.
По сути, это комментарий в ответ на ответ, который дал bincob. Я думаю, что они действительно ведут себя очень похоже, но точка зрения bincob заключалась в том, что они никоим образом не определяют интерфейс. И было предложено добавлять константы к реальным классам, а не создавать скелетный класс MyGlobalConstants. (Хотя иногда это имеет смысл; используйте «статический класс», имея статические константы и частный конструктор для предотвращения создания экземпляров; см. Java.lang.math.) Рассмотрите возможность использования enum.
Также добавление "final" в объявление класса предотвратит создание подклассов. (В C# вы можете использовать «static», что означает «окончательный абстрактный», поэтому нет необходимости в явном частном конструкторе.)
Да, @JonCoombs, но "final" не мешает прямому созданию экземпляра. И Java запрещает как final, так и abstract, появляться вместе для классов, отсюда бесконечно появляющийся частный конструктор, предотвращающий как создание экземпляров, так и создание подклассов. Я понятия не имею, почему "final abstract" запрещен, кроме того, что на первый взгляд кажется противоречивым в том, как он читается: "вы не можете создать подкласс, но этот класс предназначен для подкласса".
Основываясь на приведенных выше комментариях, я думаю, что это хороший подход для изменения старомодного глобального постоянного класса (имеющего общедоступные статические конечные переменные) на его эквивалент, подобный перечислению, следующим образом:
public class Constants {
private Constants() {
throw new AssertionError();
}
public interface ConstantType {}
public enum StringConstant implements ConstantType {
DB_HOST("localhost");
// other String constants come here
private String value;
private StringConstant(String value) {
this.value = value;
}
public String value() {
return value;
}
}
public enum IntConstant implements ConstantType {
DB_PORT(3128),
MAX_PAGE_SIZE(100);
// other int constants come here
private int value;
private IntConstant(int value) {
this.value = value;
}
public int value() {
return value;
}
}
public enum SimpleConstant implements ConstantType {
STATE_INIT,
STATE_START,
STATE_END;
}
}
Итак, я могу отнести их к следующим категориям:
Constants.StringConstant.DB_HOST
Почему? Как это улучшение? Каждая ссылка теперь громоздка (Constants.StringConstant.whatever). ИМХО, вы здесь по ухабистой дороге.
Я использую следующий подход:
public final class Constants {
public final class File {
public static final int MIN_ROWS = 1;
public static final int MAX_ROWS = 1000;
private File() {}
}
public final class DB {
public static final String name = "oups";
public final class Connection {
public static final String URL = "jdbc:tra-ta-ta";
public static final String USER = "testUser";
public static final String PASSWORD = "testPassword";
private Connection() {}
}
private DB() {}
}
private Constants() {}
}
Затем, например, я использую Constants.DB.Connection.URL для получения константы.
На мой взгляд, это более «объектно-ориентировано».
Интересно, но громоздко. Почему бы вам вместо этого не создать константы в классе, наиболее тесно связанном с ними, как рекомендуют другие? Например, в другом месте кода БД у вас есть базовый класс для ваших подключений? Например. «База подключений». Затем вы можете поместить туда константы. Любой код, работающий с соединениями, скорее всего, уже имеет импорт, который может просто сказать «ConnectionBase.URL», а не «Constants.DB.Connection.URL».
@ToolmakerSteve А как насчет общих констант, которые могут использовать несколько классов? например, стили, URL-адреса веб-служб и т. д.?
Объединение всех констант в один класс дает одно преимущество - обслуживание. Вы всегда точно знаете, где найти константы. Я не говорю, что это улучшает эту технику, я просто даю одно преимущество. А то, что сделал @ albus.ua, - это категоризация его констант, что является довольно хорошей идеей, особенно если класс констант содержит много-много постоянных значений. Этот метод поможет сохранить управляемость класса и поможет лучше описать назначение константы.
Я предпочитаю static final, я бы использовал enum только в том случае, если элемент действительно перечислим.
Я использую static final для объявления констант и использую нотацию именования ALL_CAPS. Я видел довольно много реальных примеров, когда все константы объединены в интерфейс. Несколько сообщений справедливо назвали это плохой практикой, прежде всего потому, что интерфейс не для этого. Интерфейс должен обеспечивать выполнение контракта и не должен быть местом для размещения несвязанных констант. Объединение его в класс, который не может быть создан (через частный конструктор), тоже хорошо, если семантика констант не принадлежит определенному классу ( es). Я всегда помещаю константу в класс, к которому она больше всего относится, потому что это имеет смысл и легко обслуживается.
Перечисления - хороший выбор для представления диапазона значений, но если вы храните автономные константы с акцентом на абсолютное значение (например, TIMEOUT = 100 мс), вы можете просто использовать подход static final.
Я согласен с тем, что большинство говорят, лучше всего использовать перечисления при работе с коллекцией констант. Однако, если вы программируете на Android, есть лучшее решение: Аннотация IntDef.
@Retention(SOURCE)
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST,NAVIGATION_MODE_TABS})
public @interface NavigationMode {}
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
...
public abstract void setNavigationMode(@NavigationMode int mode);
@NavigationMode
public abstract int getNavigationMode();
Аннотация IntDef превосходит перечисления одним простым способом: она занимает значительно меньше места, поскольку является просто маркером времени компиляции. Это не класс, и у него нет свойства автоматического преобразования строк.
Хотя я согласен с лучшей практикой - не использовать интерфейс только в Java и избегать enum в Android, эта замена в Android работает только при использовании небольшого количества полей. Это значительная экономия памяти, но может привести к раздуванию, поскольку вы используете интерфейс для каждого поля в смешанном перечислении. Например. если у меня есть смешанное перечисление, которое определяет, что объект принимает в своем конструкторе, я не могу ничего сохранить с этим подходом и возвращаюсь к небезопасным константам, потому что в Android вам не нужно слишком много классов / интерфейсов.
Есть определенное количество мнений, чтобы ответить на этот вопрос. Для начала, константы в java обычно объявляются общедоступными, статическими и окончательными. Ниже приведены причины:
public, so that they are accessible from everywhere
static, so that they can be accessed without any instance. Since they are constants it
makes little sense to duplicate them for every object.
final, since they should not be allowed to change
Я бы никогда не стал использовать интерфейс для средства доступа / объекта CONSTANTS просто потому, что интерфейсы обычно должны быть реализованы. Разве это не выглядело бы смешно:
String myConstant = IMyInterface.CONSTANTX;
Вместо этого я бы выбрал несколько разных способов, основанных на некоторых небольших компромиссах, и поэтому это зависит от того, что вам нужно:
1. Use a regular enum with a default/private constructor. Most people would define
constants this way, IMHO.
- drawback: cannot effectively Javadoc each constant member
- advantage: var members are implicitly public, static, and final
- advantage: type-safe
- provides "a limited constructor" in a special way that only takes args which match
predefined 'public static final' keys, thus limiting what you can pass to the
constructor
2. Use a altered enum WITHOUT a constructor, having all variables defined with
prefixed 'public static final' .
- looks funny just having a floating semi-colon in the code
- advantage: you can JavaDoc each variable with an explanation
- drawback: you still have to put explicit 'public static final' before each variable
- drawback: not type-safe
- no 'limited constructor'
3. Use a Class with a private constructor:
- advantage: you can JavaDoc each variable with an explanation
- drawback: you have to put explicit 'public static final' before each variable
- you have the option of having a constructor to create an instance
of the class if you want to provide additional functions related
to your constants
(or just keep the constructor private)
- drawback: not type-safe
4. Using interface:
- advantage: you can JavaDoc each variable with an explanation
- advantage: var members are implicitly 'public static final'
- you are able to define default interface methods if you want to provide additional
functions related to your constants (only if you implement the interface)
- drawback: not type-safe
What is the best way to implement constants in Java?
Один подход, которого нам действительно следует избегать: использование интерфейсов для определения констант.
Создание интерфейса специально для объявления констант - это на самом деле худшее: оно устраняет причину, по которой были разработаны интерфейсы: контракт определения методов.
Даже если интерфейс уже существует для решения конкретной задачи, объявление констант в них действительно не имеет смысла, поскольку константы не должны быть частью API и контракта, предоставляемого клиентским классам.
Чтобы упростить, у нас есть в целом 4 действительных подхода.
С полем static final String/Integer:
С Java 5 enum:
TL; DR: как лучше всего и где найти константы?
В большинстве случаев способ enum, вероятно, лучше, чем путь static final String/Integer и лично я считаю, что способ static final String/Integer следует использовать только в том случае, если у нас есть веские причины не использовать перечисления.
И о том, где мы должны объявить постоянные значения, идея состоит в том, чтобы выяснить, существует ли единственный существующий класс, обладающий определенной и сильной функциональной связью с постоянными значениями. Если мы найдем такой класс, мы должны использовать его в качестве держателя констант.. В противном случае константа не должна быть связана ни с одним конкретным классом.
static final String / static final Integer по сравнению с enum
Использование перечислений - действительно способ серьезно задуматься.
Перечисления имеют большое преимущество перед полями констант String или Integer.
Они устанавливают более жесткие ограничения компиляции.
Если вы определяете метод, который принимает перечисление в качестве параметра, вы можете передать только значение перечисления, определенное в классе перечисления (или null).
С помощью String и Integer вы можете заменить их любыми значениями совместимого типа, и компиляция будет правильной, даже если значение не является константой, определенной в полях static final String / static final Integer.
Например, ниже две константы, определенные в классе как поля static final String:
public class MyClass{
public static final String ONE_CONSTANT = "value";
public static final String ANOTHER_CONSTANT = "other value";
. . .
}
Вот метод, который ожидает иметь одну из этих констант в качестве параметра:
public void process(String constantExpected){
...
}
Вы можете вызвать его таким образом:
process(MyClass.ONE_CONSTANT);
или же
process(MyClass.ANOTHER_CONSTANT);
Но никакое ограничение компиляции не мешает вам вызвать его таким образом:
process("a not defined constant value");
У вас будет ошибка только во время выполнения и только если вы будете одновременно проверять переданное значение.
При использовании enum проверки не требуются, поскольку клиент может передавать только значение перечисления в параметре перечисления.
Например, здесь два значения, определенные в классе перечисления (так постоянны из коробки):
public enum MyEnum {
ONE_CONSTANT("value"), ANOTHER_CONSTANT(" another value");
private String value;
MyEnum(String value) {
this.value = value;
}
...
}
Вот метод, который ожидает иметь одно из этих значений перечисления в качестве параметра:
public void process(MyEnum myEnum){
...
}
Вы можете вызвать его таким образом:
process(MyEnum.ONE_CONSTANT);
или же
process(MyEnum.ANOTHER_CONSTANT);
Но компиляция никогда не позволит вам вызвать его таким образом:
process("a not defined constant value");
Где мы должны объявить константы?
Если ваше приложение содержит единственный существующий класс, обладающий определенной и сильной функциональной связью с постоянными значениями, то пункты 1) и 2) выглядят более интуитивно понятными. Как правило, использование констант упрощается, если они объявлены в основном классе, который ими манипулирует или у которого есть имя, очень естественное, чтобы предположить, что мы найдем его внутри.
Например, в библиотеке JDK значения экспоненты и константы pi объявлены в классе, который объявляет не только объявления констант (java.lang.Math).
public final class Math {
...
public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;
...
}
Клиенты, использующие математические функции, часто полагаются на класс Math.
Таким образом, они могут достаточно легко находить константы, а также могут запомнить, где E и PI определены очень естественным образом.
Если ваше приложение не содержит существующего класса, который имеет очень конкретную и сильную функциональную связь с постоянными значениями, варианты 1) и 2 варианта) кажутся более интуитивными.
Как правило, использование констант не упрощает, если они объявлены в одном классе, который ими манипулирует, в то время как у нас также есть 3 или 4 других класса, которые манипулируют ими в такой же степени, и ни один из этих классов не кажется более естественным, чем другие. значения констант хоста.
Здесь имеет смысл определение настраиваемого класса для хранения только постоянных значений.
Например, в библиотеке JDK перечисление java.util.concurrent.TimeUnit не объявлено в конкретном классе, поскольку на самом деле нет одного и только одного конкретного класса JDK, который кажется наиболее интуитивно понятным для его хранения:
public enum TimeUnit {
NANOSECONDS {
.....
},
MICROSECONDS {
.....
},
MILLISECONDS {
.....
},
SECONDS {
.....
},
.....
}
Многие классы, объявленные в java.util.concurrent, используют их:
BlockingQueue, ArrayBlockingQueue<E>, CompletableFuture, ExecutorService, ... и на самом деле ни один из них не кажется более подходящим для хранения enum.
просто добавить константы java: общедоступные / частные