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


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

public static final String SOME_CONST = "SOME_VALUE";

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

  1. интерфейс
  2. Абстрактный Класс
  3. Заключительный Класс

какой я должен использовать и почему?


пояснения к некоторым ответам:

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

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

10 58

10 ответов:

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

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

в другой класс :

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}

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

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

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

1) не надо. Создайте константы в фактическом классе, где они наиболее релевантны. Наличие класса/интерфейса "мешок констант" на самом деле не соответствует рекомендациям OO.

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

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

3) интерфейс это будет работать, но не мое предпочтение, дающее возможное упоминание злоупотребления в № 2.

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

Как отмечает Джошуа блох в эффективной Java:

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

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

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

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

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

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

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

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

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

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

не метки лучший выбор для таких вещей?

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

Я предпочитаю использовать [ключевое слово Java]interface на 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.";
}

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

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

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

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

понятие перечисления - это "перечисления-это наборы тесно связанных элементов".

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

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