Недавно я наткнулся на конструкцию Java, которую я никогда раньше не видел, и мне было интересно, стоит ли мне ее использовать. Кажется, это называется блоками инициализатора .
public class Test {
public Test() { /* first constructor */ }
public Test(String s) { /* second constructor */ }
// Non-static initializer block - copied into every constructor:
{
doStuff();
}
}
Блок кода будет скопирован в каждый конструктор, т.е. если у вас есть несколько конструкторов, вам не нужно переписывать код.
Тем не менее, я вижу три основных недостатка использования этого синтаксиса:
- Это один из очень немногих случаев в Java, где важен порядок вашего кода, так как вы можете определить несколько блоков кода, и они будут выполняться в порядке их написания. Это кажется мне вредным, поскольку простое изменение порядка блоков кода на самом деле изменит код.
- Я не вижу никакой пользы от его использования. В большинстве случаев конструкторы будут вызывать друг друга с некоторыми предопределенными значениями. Даже если это не так, код можно просто поместить в закрытый метод и вызвать из каждого конструктора.
- Это снижает удобочитаемость, поскольку вы можете поместить блок в конец класса, а конструктор обычно находится в начале класса. Совершенно нелогично смотреть на совершенно другую часть файла кода, если вы не ожидаете, что это будет необходимо.
Если мои приведенные выше утверждения верны, почему (и когда) была введена эта языковая конструкция? Есть ли законные случаи использования?
{ doStuff(); }
на уровне класса блок инициализатора.doStuff()
Ответы:
Есть два случая, когда я использую блоки инициализатора.
Первый предназначен для инициализации финальных членов. В Java вы можете инициализировать последний элемент либо встроенным в объявление, либо инициализировать его в конструкторе. В методе запрещено присваивать конечному члену.
Это действительно:
Это также верно:
Это неверно:
Если у вас есть несколько конструкторов, и если вы не можете инициализировать последний элемент в строке (потому что логика инициализации слишком сложна), или если конструкторы не могут вызывать себя, то вы можете либо скопировать / вставить код инициализации, либо использовать блок инициализатора.
Другой вариант использования, который у меня есть для блоков инициализатора, - для построения небольших вспомогательных структур данных. Я объявляю член и помещаю в него значения сразу после его объявления в своем собственном блоке инициализатора.
источник
squareVal = val * val
будут жаловаться на доступ к неинициализированным значениям. Блоки инициализатора не могут зависеть от аргументов, переданных конструктору. Обычное решение, которое я видел для такого рода проблем, состоит в том, чтобы определить один «базовый» конструктор со сложной логикой и определить все другие конструкторы в терминах этого. Фактически, большинство применений инициализаторов экземпляров могут быть заменены этим шаблоном.В общем, не используйте нестатические блоки инициализатора (и, возможно, избегайте статических).
Запутанный синтаксис
Глядя на этот вопрос, есть 3 ответа, но вы обманули 4 человек с этим синтаксисом. Я был одним из них, и я пишу на Java в течение 16 лет! Очевидно, что синтаксис потенциально подвержен ошибкам! Я бы держался подальше от этого.
Телескопические Конструкторы
Для действительно простых вещей вы можете использовать «телескопические» конструкторы, чтобы избежать этой путаницы:
Образец Строителя
Если вам нужно сделать doStuff () в конце каждого конструктора или другой сложной инициализации, возможно, будет лучше использовать шаблон компоновщика. Джош Блох перечисляет несколько причин, почему строители - хорошая идея. Строителям требуется немного времени для написания, но правильно написано, что ими приятно пользоваться.
Петли статического инициализатора
Я использовал статический инициализаторы, но иногда сталкивался с циклами, в которых 2 класса зависели от того, как блоки статического инициализатора друг друга вызывались до того, как класс мог быть полностью загружен. Это приводило к «не удалось загрузить класс» или аналогично расплывчатое сообщение об ошибке. Мне пришлось сравнить файлы с последней известной рабочей версией в системе контроля версий, чтобы выяснить, в чем проблема. Не весело вообще.
Ленивая инициализация
Возможно, статические инициализаторы хороши по причинам производительности, когда они работают, и не слишком запутаны. Но в целом я предпочитаю ленивую инициализацию статическим инициализаторам в эти дни. Понятно, что они делают, я еще не сталкивался с ними при загрузке классов, и они работают в большем количестве ситуаций инициализации, чем блоки инициализатора.
Определение данных
Вместо статической инициализации для построения структур данных (сравните с примерами в других ответах), теперь я использую вспомогательные функции определения неизменных данных Paguro :
Conculsion
В начале Java блоки инициализатора были единственным способом сделать что-то, но теперь они сбивают с толку, подвержены ошибкам и в большинстве случаев были заменены лучшими альтернативами (подробно описано выше). Интересно узнать о блоках инициализатора, если вы видите их в унаследованном коде или они проходят тестирование, но если бы я делал обзор кода и видел один в новом коде, я бы попросил вас объяснить, почему ни один из вышеупомянутые альтернативы были подходящими, прежде чем дать ваш код большой палец вверх.
источник
В дополнение к инициализации переменной экземпляра, которая объявлена как
final
(см . Ответ Барджака ), я бы также упомянулstatic
блок инициализации.Вы можете использовать их как своего рода «статический конструктор».
Таким образом, вы можете выполнять сложные инициализации статической переменной при первом обращении к классу.
Вот пример, вдохновленный примером Барджака:
источник
Что касается нестатических блоков инициализатора, их голая функция - выступать в качестве конструктора по умолчанию в анонимных классах. Это в основном их единственное право на существование.
источник
Я полностью согласен с утверждениями 1, 2, 3. Я также никогда не использую блочные инициализаторы по этим причинам, и я не знаю, почему он существует в Java.
Однако я вынужден использовать инициализатор статического блока в одном случае: когда мне нужно создать экземпляр статического поля, конструктор которого может вызвать проверенное исключение.
Но вместо этого вы должны сделать:
Я нахожу эту идиому очень уродливой (она также мешает вам пометить
context
какfinal
), но это единственный способ, поддерживаемый Java для инициализации таких полей.источник
context = null;
в своем блоке catch, вы сможете объявить контекст как окончательный.The final field context may already have been assigned
static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; }