Что предпочтительнее Throwable для использования в конструкторе частного класса?

Эффективное Java (второе издание), пункт 4, обсуждает использование частных конструкторов для обеспечения неинтересности. Вот пример кода из книги:

public final class UtilityClass {
    private UtilityClass() {
        throw new AssertionError();
    }
}

Однако AssertionError не похоже на то, что нужно бросить. Ничто не утверждается, так как API определяет использование AssertionError.

Существует ли в этой ситуации другой Throwable? Обычно ли вы просто бросаете общий Exception с сообщением? Или это типично для написания пользовательского Exception для этого?

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

Ответ 1

Существует утверждение: "Я утверждаю, что этот конструктор никогда не будет вызван". Итак, действительно, AssertionError здесь верен.

Ответ 2

Мне нравится включить комментарий Блоха:

// Suppress default constructor for noninstantiability

Или еще лучше поместить его в Error:

private UtilityClass()
{
    throw new AssertionError("Suppress default constructor for noninstantiability");
}

Ответ 3

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

Ответ 5

Нет, нет, со всем уважением к Джошу Блоху, никогда не бросайте AssertionError, если это не будет из утверждения. Если вы хотите здесь AssertionError, бросьте его с помощью assert(false). Затем кто-то, читающий код, может найти его позже.

Еще лучше, определите свое собственное исключение, скажем CantInstantiateUtilityClass. то у вас будет код, который говорит

try {
    // some stuff
} catch (CantInstantiateUtilityClass e) {
    // react
}

чтобы читатель зрелище знал, что произошло.

Обновление

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

Ответ 6

Когда код требует включения JUnit в качестве зависимости, например, в область теста maven <scope>test</scope>, тогда переходите прямо к методу Assertion.fail() и извлекайте выгоду из значительного улучшения ясности.

public final class UtilityClass {
    private UtilityClass() {
        fail("The UtilityClass methods should be accessed statically");
    }
}

Вне области тестирования вы можете использовать что-то вроде следующего, что потребует статического импорта для использования, как описано выше. import static pkg.Error.fail;

public class Error {
    private static final Logger LOG = LoggerFactory.getLogger(Error.class);
    public static void fail(final String message) {
        LOG.error(message);
        throw new AssertionError(message);
        // or use your preferred exception 
        // e.g InstantiationException
    }
}

Какое следующее использование.

public class UtilityClassTwo {
    private UtilityClassTwo() {
        Error.fail("The UtilityClass methods should be accessed statically");
    }
}

В самой идиоматической форме все они сводятся к следующему:

public class UtilityClassThree {
    private UtilityClassThree() {
        assert false : "The UtilityClass methods should be accessed statically";
    }
}

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

 private Constructor() {
    throw new UnsupportedOperationException(
            "Do not instantiate this class, use statically.");
}

Ответ 7

Сломанное утверждение означает, что вы нарушили спецификацию контракта вашего кода. Так что это правильно.

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

Ответ 8

Вы можете создать свой собственный класс, расширяющий Throwable, например:

class NoninstantiabilityError extends Throwable

Это имеет следующие преимущества:

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

Пример использования:

public final class UtilityClass {
    private UtilityClass() throws NoninstantiabilityError {
        throws new NoninstantiabilityError();
    }

    ...
}