Подтвердить что ты не робот

Разница между java enum без значений и класс утилиты с частным конструктором

Общепринятым для служебных классов является предоставить им частный конструктор:

public final class UtilClass {
    private UtilClass() {}

    ...
}

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

Многие из этих предупреждений исчезают, если вы это сделаете:

public enum UtilClass {;
    ...
}

Мой вопрос: помимо бесконечной ненависти к будущим разработчикам, какие важные отличия между enum без значений и класса с частным конструктором в Java?

Обратите внимание, что я не спрашиваю Какое преимущество переименования Java в сравнении с классом с публичными статическими конечными полями?. Я не решаю, должен ли список вещей быть связкой констант или перечислением, я решаю между тем, чтобы положить кучу функций в класс, не содержащий конструктора, или перечисление без значения.

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

Например, использование перечисления загрязняет автозаполнение бесполезными методами, например UtilClass.values(). Какие еще недостатки? Расквитаться?

4b9b3361

Ответ 1

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

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

Этот бит от "Эффективной Java" Джошуа Блоха о таких синглонах также относится к классам утилиты:

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

Отказ от ответственности: я не использовал этот шаблон и не рекомендую его или против него.

Ответ 2

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

Шаблон "полезный класс" совершенно закончен. Если вашим инструментам это не нравится, проблема с инструментами.

Ответ 3

Следующий паттерн в классах утилит также обеспечивает конфиденциальную гарантию того, что экземпляры не могут быть:

public abstract class Util {
    private Util() { throw new Error(); }
    ... // static methods
}

Кроме того, у вас нет дополнительных неулокальных статических методов, предоставляемых перечислениями.

Ответ 4

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

public final class UtilityClass {

    public static final UtilityClass Instance = new UtilityClass();

    private UtilityClass () {}

    public static int Foo (int a, int b) {
         return a+b;
    }

}

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

В целом, большинство книг по дизайну программного обеспечения, которые я когда-либо читал, не используют статические методы. Если они не являются действительно полезными методами: в том смысле, что они никогда никогда не требуют какого-либо состояния. И даже тогда это лишь небольшое усилие для реализации шаблона Singleton, так что, когда придет время, вы можете назначить ему состояние:

public final class UtilityClass {

    public static final UtilityClass Instance = new UtilityClass();

    private UtilityClass () {}

    public int Foo (int a, int b) {
         return a+b;
    }

}

И назовем его UtilityClass.Instance.Foo(2,5);. После этого в процессе кодирования было бы сложнее выполнить преобразование состояния. Таким образом, статические методы сложнее поддерживать.

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

Кроме того, некоторые исследователи безопасности утверждают, что сложнее анализировать код с помощью модификаторов static, поскольку к ним можно получить доступ из любого места, а побочные эффекты менее предсказуемы (например, в автоматическом анализе безопасности): скажем, у вас есть класс который не полностью реализован, тогда вы все равно можете анализировать поля, чтобы узнать, к каким методам он может получить доступ, и таким образом проанализировать возможные побочные эффекты (использование сети, файл IO,...). Это может привести к появлению списка возможных опасностей для каждого класса, который должен быть проверен. По крайней мере, если я правильно понял кандидатскую диссертацию одного из моих коллег-исследователей. Таким образом, нестатические методы позволяют проводить больше анализа модификаторов.

В заключение: Java построен по принципу объектно-ориентированного программирования. Это означает, что " c мир мира" используется c ompiler, а "мир i nStance" i nterpreter/выполнения. Я согласен, что между двумя словами существует много конфликтов. Но методы static во многих случаях или в некоторых случаях являются ошибкой для разрешения таких конфликтов.