Как вы определяете класс констант в Java?

89

Предположим, вам нужно определить класс, который все, что он делает, хранит константы.

public static final String SOME_CONST = "SOME_VALUE";

Каков предпочтительный способ сделать это?

  1. Интерфейс
  2. Абстрактный класс
  3. Финальный класс

Какой из них использовать и почему?


Разъяснения к некоторым ответам:

Перечисления - я не буду использовать перечисления, я ничего не перечисляю, а просто собираю некоторые константы, которые никак не связаны друг с другом.

Интерфейс - я не собираюсь устанавливать какой-либо класс как тот, который реализует интерфейс. Просто хочу , чтобы использовать интерфейс для вызова констант следующим образом: ISomeInterface.SOME_CONST.

Юваль Адам
источник
Здесь есть похожее обсуждение: stackoverflow.com/questions/320588/… . Я бы использовал последний класс с частным конструктором, чтобы его нельзя было создать.
Дэн Дайер,
2
Извините, но фраза «Я не буду использовать перечисления» превращает этот вопрос в «как лучше всего сделать что-нибудь глупое?»
cletus
Я не говорю, что вы собираетесь реализовать интерфейс. Но нет смысла использовать для этого интерфейс. Итак, идите с финальным классом
:)
В чем проблема с Enum? Вы всегда можете использовать его для сбора «некоторых констант, которые никак не связаны друг с другом». Хм?
gedevan
5
По идее, перечисление - плохой выбор, если константы не связаны между собой. Перечисление представляет альтернативные значения того же типа. Эти константы не являются альтернативами и могут даже быть разных типов (некоторые могут быть строками, некоторые целыми числами и т. Д.)
Дэн Дайер

Ответы:

92

Используйте последний класс. для простоты вы можете затем использовать статический импорт для повторного использования ваших значений в другом классе

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

в другом классе:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}
user54579
источник
4
В чем преимущество создания здесь отдельного класса? Хотя я обычно не считаю Calendar API хорошим примером дизайна, это нормально с точки зрения «константы, связанные с календарем, находятся в классе Calendar».
Джон Скит,
7
Преимущество заключается в том, что код не дублируется, если вам нужно повторно использовать константы более чем в одном классе. Думаю, вы легко можете увидеть в этом преимущество.
user54579
2
Зачем дублировать код? Просто обратитесь к другому классу. Я нахожу относительно редко, когда константа действительно стоит особняком.
Джон Скит,
14
Для лучшей читабельности у меня также есть частный конструктор по умолчанию без тела (и соответствующего комментария).
Ран Бирон,
5
Довольно старый ответ, и этот комментарий, вероятно, неуместен, но я бы использовал, VALUE1.equals(variable)поскольку он избегает NPE.
Aakash 07
38

В вашем пояснении сказано: «Я не буду использовать перечисления, я ничего не перечисляю, просто собираю некоторые константы, которые никак не связаны друг с другом».

Если константы никак не связаны друг с другом, зачем вам собирать их вместе? Поместите каждую константу в класс, с которым она наиболее тесно связана.

Джон Скит
источник
2
Джон - они связаны в том смысле, что все принадлежат одной и той же функциональности. Однако они не являются перечислением чего-либо ... Они не обладают тем свойством, что объект является «одной из этих вещей».
Yuval Adam
13
Ладно. В этом случае просто поместите их в класс, наиболее близкий к функциональности, с которой они связаны.
Джон Скит,
26

Мои предложения (в порядке убывания предпочтения):

1) Не делай этого . Создайте константы в реальном классе там, где они наиболее актуальны. Наличие класса / интерфейса «мешка констант» на самом деле не соответствует лучшим практикам объектно-ориентированного проектирования.

Я, как и все остальные, время от времени игнорирую №1. Если вы собираетесь это сделать, то:

2) final класс с частным конструктором. Это, по крайней мере, предотвратит злоупотребление вашим «набором констант», расширяя / реализуя его, чтобы получить легкий доступ к константам. (Я знаю, вы сказали, что не сделаете этого, но это не значит, что кто-то придет после вас)

3) interface Это будет работать, но я не предпочитаю упоминать возможное злоупотребление в №2.

В общем, то, что это константы, не означает, что вы не должны применять к ним обычные принципы oo. Если никто, кроме одного класса, не заботится о константе - она ​​должна быть закрытой и принадлежать этому классу. Если только тесты заботятся о константе - она ​​должна быть в тестовом классе, а не в производственном коде. Если константа определена в нескольких местах (а не просто случайно в одном и том же) - произведите рефакторинг, чтобы исключить дублирование. И так далее - относитесь к ним, как к методу.

kenj0418
источник
2
Проблема 1 заключается в том, что иногда вы вставляете в свой код некоторые зависимости для другого уровня класса, просто чтобы получить константу.
amdev
Все поля в интерфейсе неявно общедоступны, статичны и окончательны
Kodebetter
@Kodebetter Но интерфейсы могут быть расширены / реализованы для добавления дополнительных полей.
Вит,
12

Как отмечает Джошуа Блох в Effective Java:

  • Интерфейсы следует использовать только для определения типов,
  • абстрактные классы не препятствуют созданию экземпляров (их можно разделить на подклассы и даже предположить, что они предназначены для создания подклассов).

Вы можете использовать Enum, если все ваши константы связаны (например, имена планет), поместить значения констант в классы, с которыми они связаны (если у вас есть к ним доступ), или использовать не поддающийся инстанции служебный класс (определите частный конструктор по умолчанию) .

class SomeConstants
{
    // Prevents instanciation of myself and my subclasses
    private SomeConstants() {}

    public final static String TOTO = "toto";
    public final static Integer TEN = 10;
    //...
}

Затем, как уже было сказано, вы можете использовать статический импорт для использования ваших констант.

Себастьян РоккаСерра
источник
7

Я предпочитаю вообще не делать этого. Возраст констант практически умер, когда в Java 5 были введены типы безопасных перечислений. И еще до этого Джош Блох опубликовал (чуть более многословную) версию этого, которая работала на Java 1.4 (и ранее).

Если вам не нужна совместимость с каким-либо унаследованным кодом, действительно нет причин использовать именованные строковые / целочисленные константы.

Cletus
источник
2
Хороший ответ, лучше пример.
amdev
3

Просто используйте последний класс.

Если вы хотите иметь возможность добавлять другие значения, используйте абстрактный класс.

Использование интерфейса не имеет особого смысла, интерфейс должен указывать контракт. Вы просто хотите объявить некоторые постоянные значения.

Megacan
источник
3

Разве перечисления не лучший выбор для такого рода вещей?

Борис Павлович
источник
2
99% времени. Но не всегда.
Л. Холанда
3

enums в порядке. IIRC, один элемент в эффективной Java (2-е изд.) Имеет enumконстанты, перечисляющие стандартные параметры, реализующие [ключевое слово Java] interfaceдля любого значения.

Я предпочитаю использовать [ключевое слово Java] вместо interfacea final classдля констант. Вы неявно получаете public static final. Некоторые люди будут утверждать, что он interfaceпозволяет плохим программистам реализовать его, но плохие программисты будут писать отстойный код, что бы вы ни делали.

Что выглядит лучше?

public final class SomeStuff {
     private SomeStuff() {
         throw new Error();
     }
     public static final String SOME_CONST = "Some value or another, I don't know.";
}

Или:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}
Том Хотин - tackline
источник
но плохие программисты будут писать отстойный код, что бы вы ни делали.Это правда. Если вы можете предпринять шаги, чтобы уменьшить плохое программирование или полностью исключить его, то вы несете определенную ответственность за это; быть как можно более явным. Плохой программист не может расширить последний класс.
liltitus27
1
@ liltitus27 В некоторой степени согласен. Но действительно ли вы хотите, чтобы уродливый код просто помешал плохим программистам писать плохой код? Код, который они создают, по-прежнему будет безнадежным, но теперь ваш код менее читабелен.
Tom Hawtin - tackline
1

Или 4. Поместите их в класс, содержащий логику, которая больше всего использует константы.

... извините, не удержался ;-)

Саймон Гроенвольт
источник
3
Это плохая идея. Это создает зависимости между классами, которых не должно быть.
Yuval Adam
Почему нет? Я бы сказал, что классы, использующие одни и те же константы, в любом случае имеют зависимость ... И каким-то образом должен быть один класс, который больше всего зависит от этих констант.
Саймон Гроенвольт,
Если бы константы использовал только один класс, это имело бы смысл.
Tom Hawtin - tackline
-1
  1. Одним из недостатков частного конструктора является то, что метод не может быть протестирован.

  2. Enum по своей природе хорошо подходит для применения в конкретном типе домена, его применение к децентрализованным константам выглядит недостаточно хорошо

Концепция Enum такова: «Перечисления - это наборы тесно связанных элементов».

  1. Расширять / реализовывать постоянный интерфейс - плохая практика, трудно думать о необходимости расширения неизменной константы вместо того, чтобы ссылаться на нее напрямую.

  2. Если применить качественный инструмент, такой как SonarSource, есть правила, вынуждающие разработчика отказаться от постоянного интерфейса, это неудобно, так как многие проекты пользуются постоянным интерфейсом и редко можно увидеть, как что-то «расширяется» на постоянных интерфейсах.

Гуйчжоу Фэн
источник
1
«частный конструктор - наличие метода никогда не может быть проверено»: неверно. Если вы (или ваш босс) действительно параноик насчет того, чтобы отчеты о покрытии кода составляли 100%, вы все равно можете проверить это, используя отражение и убедившись, что конструктор генерирует исключение. Но, IMHO, это всего лишь формальность и особо не нуждается в проверке. Если вас (или команду) действительно волнует только то, что действительно важно, 100% покрытие линии не обязательно.
Л. Холанда