Конечное поле с пометкой @NotNull не инициализировано
У меня есть этот код:
public static class MyWebDriver extends RemoteWebDriver {
@NotNull
private final String nodeId;
public MyRemoteWebDriver(@NotNull String nodeId) {
super();
this.nodeId = nodeId;
}
@Override
public void quit() {
System.out.println("deleting node: " + nodeId);
}
}
и он гарантировал, что nodeId
, передаваемый в конструктор, не null
. И поскольку поле nodeId
final
, я ожидаю, что оно будет инициализировано в моем методе quit()
.
Но в конструкторе super()
есть блок try-catch
, который в случае исключения вызывает метод quit()
и генерирует исключение. И в этом случае nodeId
, который я получаю в моем методе quit()
, не инициализирован (имеет значение null
).
Есть ли способы избежать этого, кроме
@Override
public void quit() {
if (nodeId != null) {
System.out.println("deleting node: " + nodeId);
}
}
этот? Это выглядит довольно глупо, потому что nodeId
помечен как @NotNull
.
Ответы
Ответ 1
Но в конструкторе super() есть блок try-catch, который в случае исключения вызывает quit()
Это две проблемы в одном:
-
Конструкторы
-
не должны выполнять какую-либо работу, кроме сохранения заданных параметров в переменных (final
).
[править]
Означает ли это, что классы не должны проверять свои входы? Я знаю многих людей, которые не согласятся с тем, что все их объекты могут оказаться недействительными, а не вообще отсутствовать. - chris
Под "не должно работать" я подразумеваю, что конструктор не должен вычислять какое-либо значение свойства из параметров или вызывать зависимость или не публичный метод для проверки.
Конструкторы -
никогда не должны ссылаться на методы, отличные от private
и/или final
(внутри класса), прямо или косвенно (т.е. вы не должны вызывать метод final
, который, в свою очередь, вызывает не final
).
Причиной, по которой вы сталкиваетесь с этой проблемой, является нарушение единого шаблона ответственности и разделения проблем.
Что бы вы делали в конструкторе суперклассов, скорее всего, должно быть сделано в отдельном классе, и только результаты этого процесса должны быть переданы в ваш класс (и его суперкласс).
Это, очевидно, означает, что метод quit()
также относится к другому классу.
Ответ 2
Это, конечно, легко объясняется тем, как работает Java. Объект типа MyWebDriver
не инициализируется, включая его поля, то есть в вашем случае nodeId
, пока его поля, унаследованные от суперкласса, не будут инициализированы, то есть в вашем случае до тех пор, пока super()
не вернется.
Итак, если выбрано исключение в super
, ясно, что nodeId
будет null
.
Я не думаю, что есть какое-либо решение (кроме некоторых, похожих на то, что вы предлагаете), если только используемая вами фреймворк (который вы не указали) обеспечивает некоторое обходное решение.
Ответ 3
Вы можете переместить логику инициализации из супер-конструктора в защищенный метод init() и вызвать его после инициализации nodeId.
Возможно, здесь может быть использован более четкий дизайн, но трудно представить что-либо без полного примера.
Ответ 4
Вы должны изменить bean за RemoteWebDriver
, добавив к quit
это булевское сравнение. Или даже лучше, вы не должны вызывать какие-либо методы в вашем конструкторе, а только создавать сами настройки экземпляра. После этого оценивается значение новой переменной экземпляра boolean isItFullyConstructed
, а если false, вызовите исходную логику try/catch, которая вызывает проблемы. Это выполнимо?