Типичное создание объектов в Java:
Person joe = new Person();
joe.setFirstName("Joe");
joe.setLastName("Doe");
joe.setAge(33);
В то время как в C# это выглядит так:
var joe = new Person { FirstName = "Joe", LastName = "Doe", Age = 33 };
Чуть проще, не правда ли?
Недавно в исходниках на работе я наткнулся на интересную конструкцию, которая привлекла моё внимание:
Person joe = new Person() {{ setFirstName("Joe"); setLastName("Doe"); setAge(33); }};
Хм, странно, конечно, двойные фигурные скобочки? Но уж очень мне это напоминает код на C#.
Я обрадовался — для меня это выглядит получше четырёхстрочной инициализации. Однако, немного разобравшись в смысле этой конструкции, моя радость быстро превратилась в... что-то вроде подозрения.
Что на самом деле происходит за кулисами?
В Java есть концепция анонимных классов, позволяющая создавать уникальные безымянные классы в месте вызова и тут же их использовать. Это используется почти повсеместно, в основном для выполнения функций, аналогичных делегатам в C# — например, в качестве обратного вызова или обработчика события.
Описание анонимных классов имеет следующий синтаксис:
Runnable anonym = new Runnable() {
public void run() {
// do stuff
}
};
Отметим так же, что Runnable является интерфейсом (описывающим паттерн Command), а не классом. То есть мы можем реализовать даже интерфейс где нам угодно и дальше манипулировать им как любой другой переменной.
Помимо этого каждый класс в Java может иметь два блока инициализации: статический и экземплярный (в C# есть статические конструкторы), например:
public class Test {
static { staticField = " 22"; } // static initialization
{ field = " asd"; } // instance initialization
static String staticField;
String field;
}
Я думаю, уже потихоньку становится понятно, что же на самом деле происходит в примере с инициализацией в начале поста.
Мы объявляем анонимный класс-наследник Person и в блоке инициализации экземпляра вызываем нужные нам сеттеры. То есть, мы реально создаем новый класс, а не инициализируем уже имеющийся.
К сожалению, не могу сказать, каждый ли раз при вызове одного и того же кода с такой инициализацией создается новый класс (не экземпляр, а класс), но в любом случае злоупотребление таким методом создания может привести к ошибке OutOfMemoryException: PermGen space, которая возникает, когда в JVM заканчивается область памяти в которой хранятся описания классов не входящих в язык (Permanent Generation, по умолчанию 64 мб).
Спасибо!
Хорошего дня!