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

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

public static final String SOME_CONST = "SOME_VALUE";

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

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

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


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

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

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


person Yuval Adam    schedule 26.01.2009    source источник
comment
Здесь есть похожее обсуждение: stackoverflow.com/questions/320588/. Я бы использовал последний класс с частным конструктором, чтобы его нельзя было создать.   -  person Dan Dyer    schedule 26.01.2009
comment
Извините, но я не собираюсь использовать перечисления, превращает этот вопрос в лучший способ сделать что-то глупое?   -  person cletus    schedule 26.01.2009
comment
Я не говорю, что вы собираетесь реализовать интерфейс. Но нет смысла использовать для этого интерфейс. Итак, приступим к последнему классу :)   -  person Megacan    schedule 26.01.2009
comment
В чем проблема с Enum? Вы всегда можете использовать его для сбора «некоторых констант, которые никак не связаны друг с другом». Хм?   -  person gedevan    schedule 26.01.2009
comment
По идее, перечисление - плохой выбор, если константы не связаны между собой. Перечисление представляет альтернативные значения того же типа. Эти константы не являются альтернативами, и они могут даже не быть одного и того же типа (некоторые могут быть строками, некоторые целыми числами и т. Д.)   -  person Dan Dyer    schedule 26.01.2009
comment
Я не думаю, что enum - плохой выбор для замены public static final String SOME_CONST = SOME_VALUE. Даже если константы не связаны. Не вижу здесь проблем.   -  person gedevan    schedule 14.02.2009


Ответы (11)


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

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

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

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}
person user54579    schedule 26.01.2009
comment
Какая польза от создания здесь отдельного класса? Хотя обычно я не считаю Calendar API хорошим примером дизайна, с точки зрения констант, связанных с календарем, он есть в классе Calendar. - person Jon Skeet; 26.01.2009
comment
Преимущество заключается в том, что код не дублируется, если вам нужно повторно использовать константы более чем в одном классе. Думаю, вы легко можете увидеть в этом преимущество. - person user54579; 26.01.2009
comment
Зачем дублировать код? Просто обратитесь к другому классу. Я нахожу относительно редко, когда константа действительно стоит особняком. - person Jon Skeet; 26.01.2009
comment
Для лучшей читаемости у меня также будет частный конструктор по умолчанию без тела (и соответствующего комментария). - person Ran Biron; 26.01.2009
comment
Довольно старый ответ, и этот комментарий, вероятно, неуместен, но я бы использовал VALUE1.equals(variable), поскольку он избегает NPE. - person Aakash; 07.06.2017

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

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

person Jon Skeet    schedule 26.01.2009
comment
Джон - они связаны в том смысле, что все принадлежат одной и той же функциональности. Однако они не являются перечислением чего-либо ... Они не обладают тем свойством, что объект является одной из этих вещей. - person Yuval Adam; 26.01.2009
comment
Хорошо. В этом случае просто поместите их в класс, наиболее близкий к функциональности, с которой они связаны. - person Jon Skeet; 26.01.2009

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

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

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

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

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

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

person kenj0418    schedule 07.04.2009
comment
Проблема 1 заключается в том, что иногда вы вставляете в свой код некоторые зависимости для другого уровня класса, просто чтобы получить константу. - person amdev; 10.07.2018
comment
Все поля в интерфейсе неявно общедоступны, статичны и окончательны. - person Kodebetter; 16.04.2019
comment
@Kodebetter Но интерфейсы могут быть расширены / реализованы для добавления дополнительных полей. - person Wit; 08.06.2020

Как отмечает Джошуа Блох в 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;
    //...
}

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

person Sébastien RoccaSerra    schedule 26.01.2009

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

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

person cletus    schedule 26.01.2009
comment
Хороший ответ, лучше образец. - person amdev; 10.07.2018

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

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

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

person Megacan    schedule 26.01.2009

Не являются перечисления лучшим выбором для этих какие вещи?

person Boris Pavlović    schedule 26.01.2009
comment
99% времени. Но не всегда. - person L. Holanda; 03.05.2016

enums в порядке. IIRC, один элемент в эффективной Java (2-е изд.) Имеет 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.";
}

Or:

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

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

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

person Simon Groenewolt    schedule 26.01.2009
comment
Это плохая идея. Это создает зависимости между классами, которых не должно быть. - person Yuval Adam; 26.01.2009
comment
Почему нет? Я бы сказал, что классы, использующие одни и те же константы, в любом случае имеют зависимость ... И каким-то образом должен быть один класс, который больше всего зависит от этих констант. - person Simon Groenewolt; 27.01.2009
comment
Если бы константы использовал только один класс, это имело бы смысл. - person Tom Hawtin - tackline; 13.11.2013

Лучший подход для меня - enum:

public enum SomeApiConstants {;

    public static final String SOME_CONST = "SOME_VALUE";

    //may be in hierarchy
    public enum ApiMapping {;
        public static final String VERSION = "/version";
        public static final String VERSION_LIST = "/list/{type}";
    }
}

Плюсы:

  • чистый код
  • частный конструктор не нужно определять
  • попытка создать экземпляр проверяется во время компиляции как java: enum types may not be instantiated
  • предотвращает клонирование и десериализацию
person kapitanrum    schedule 09.07.2021

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

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

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

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

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

person Guizhou Feng    schedule 11.09.2015
comment
частный конструктор - существование метода никогда не может быть проверено: неверно. Если вы (или ваш начальник) действительно параноидально относитесь к 100% отчетам о покрытии кода, вы все равно можете проверить это, используя отражение и убедившись, что конструктор генерирует исключение. Но, ИМХО, это всего лишь формальность и особо не нуждается в проверке. Если вас (или команду) действительно волнует только то, что действительно важно, 100% покрытие линии не обязательно. - person L. Holanda; 03.05.2016