Что предпочтительнее использовать Throwable в частном конструкторе служебных классов? - PullRequest
24 голосов
/ 30 декабря 2008

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

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

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

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

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

Ответы [ 8 ]

28 голосов
/ 30 декабря 2008

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

10 голосов
/ 30 декабря 2008

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

// Suppress default constructor for noninstantiability

Или, что еще лучше, добавив ошибку:

private UtilityClass()
{
    throw new AssertionError("Suppress default constructor for noninstantiability");
}
3 голосов
/ 30 декабря 2008

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

3 голосов
/ 30 декабря 2008

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

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

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

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

Обновление

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

2 голосов
/ 30 декабря 2008

А как насчет IllegalAcessError ? :)

1 голос
/ 15 июля 2018

Когда код требует включения 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.");
}
0 голосов
/ 24 января 2019

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

class NoninstantiabilityError extends Throwable

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

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

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

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

    ...
}
0 голосов
/ 30 декабря 2008

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

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

...