Какой предпочтительный Throwable для использования в конструкторе частного служебного класса?
-
29-08-2019 - |
Вопрос
Эффективная Java (Второе издание), Пункт 4, обсуждает использование частных конструкторов для обеспечения невозможности установки.Вот пример кода из книги:
public final class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
Однако, AssertionError
не похоже, что это то, что нужно выбрасывать.Ничто не "утверждается", именно так API определяет использование Ошибка утверждения.
Есть ли другой Throwable
это типично в такой ситуации?Обычно кто-то просто бросает общий Exception
с сообщением?Или это обычное дело - писать пользовательский Exception
за это?
Это довольно тривиально, но больше всего, я думаю, мне просто интересно узнать об этом с точки зрения стиля и стандартов.
Решение
Есть утверждение:"Я утверждаю, что этот конструктор никогда не будет вызван".Так что, действительно, AssertionError
здесь это правильно.
Другие советы
Мне нравится включать комментарий Блоха:
// Suppress default constructor for noninstantiability
Или еще лучше поместить это в сообщение Об ошибке:
private UtilityClass()
{
throw new AssertionError("Suppress default constructor for noninstantiability");
}
Исключение UnsupportedOperationException звучит как наилучший вариант, хотя проверяемое исключение было бы еще лучше, поскольку оно могло бы предупредить кого-то, ошибочно создающего экземпляр класса во время компиляции.
О чем Ошибка незаконного доступа ? :)
Нет-нет-нет, при всем моем уважении к Джошу Блоху, никогда не бросайте AssertionError
если только это не из утверждения. Если вам нужен AssertionError здесь, выбросьте его с assert(false)
.Тогда кто-то, читающий код, сможет найти его позже.
Еще лучше, определите свое собственное исключение, скажем CantInstantiateUtilityClass
.тогда у вас будет код, который гласит
try {
// some stuff
} catch (CantInstantiateUtilityClass e) {
// react
}
чтобы читатель " ловца " знал что случилось.
Обновить
Время от времени какой-нибудь чертов дурак забредает сюда и снова отрицает это, спустя почти четыре года после свершившегося факта.Итак, позвольте мне просто отметить, что стандартный все еще определяет AssertionError
как результат неудачного утверждения, а не как думает какой-нибудь новичок следовало бы должен быть выброшен вместо четко определенного информативного исключения.К сожалению, хорошая дисциплина исключения, пожалуй, наименее поощряемый навык в программировании на Java.
Когда код требует включения 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.");
}
Нарушенное утверждение означает, что вы нарушили спецификацию контракта вашего кода.Так что здесь все правильно.
Однако, поскольку я предполагаю, что вы будете создавать экземпляр в частном порядке, он также вызовет конструктор и вызовет ошибку - если у вас нет другого конструктора?
Вы можете создать свой собственный класс, расширяющий Throwable
, например:
class NoninstantiabilityError extends Throwable
Это имеет следующие преимущества:
- Название указывает на проблему
- Потому что это непосредственно расширяет
Throwable
маловероятно, что он будет пойман случайно - Потому что это непосредственно расширяет
Throwable
это проверяется, и случайный вызов соответствующего конструктора потребовал бы перехвата исключения
Пример использования:
public final class UtilityClass {
private UtilityClass() throws NoninstantiabilityError {
throw new NoninstantiabilityError();
}
...
}