#java #android #coding-style
#java #Android #стиль кодирования
Вопрос:
Мне было интересно, оптимизирует ли компилятор (ECJ, javac или вы можете назвать свой любимый компилятор) анонимные классы, которые не добавляют или переопределяют методы своего базового класса.
Например, для кода, который выглядит следующим образом:
Фрагмент A:
Human h = new Human("John", 30);
h.setX(12.5);
h.setY(15.3);
//..
Eat(h);
Я бы всегда предпочел синтаксис:
Фрагмент B:
Eat(new Human("John", 30){
{
setX(12.5);
setY(15.3);
//..
}
});
Однако я понимаю, что в отличие от WITH
ключевого слова, которое мы имеем в vb.net это не просто синтаксический сахар. То, что мы говорим компилятору сделать, это создать анонимный подкласс Human, конструктор которого состоит из кода внутри bracers (именно поэтому мы не можем использовать этот синтаксический сахар для конечных классов).
Проблема сейчас в том, что я постоянно использую этот синтаксический сахар (например, переопределяя onclick в прослушивателях пользовательского интерфейса и т.д.), Это похоже на один из моих стилей / привычек кодирования.
Отсюда вопрос:
-
Оптимизирует ли компилятор такой синтаксис? (т. Е. Он понял, что никаких анонимных классов генерировать не нужно, а производительность фрагмента B будет такой же, как у фрагмента A)
-
Если ответ на (1) «нет», мне было интересно, оказывает ли это (более чем ожидаемое обилие анонимных классов из-за этого стиля кодирования) заметное влияние, так что настоятельно рекомендуется, чтобы для будущих проектов (кодирование приложений для среднего мобильного устройства) мы всегда придерживались стиля в фрагменте A?
Комментарии:
1. К сожалению, у меня нет никакого представления о том, как компилятор справится с этим. Но вы могли бы сами написать цикл, который выполняет это в обоих направлениях много раз, и провести измерения, чтобы увидеть последствия обоих для производительности.
2. @Tim Да, но единственное, что делать подобные тесты совсем не точно. Например, в реальном приложении такой код не будет выполняться в цикле в части кода, а скорее в виде фрагментов по всему всему коду с начала срока службы приложения до конца срока службы приложения (это аналогично нашему использованию операторов
if
andelse
в фрагментах всего кода вместо, скажем, как в цикле)3. Просто примечание: вы определяете не конструктор, а блок инициализатора. Эти блоки выполняются перед конструктором! Таким образом, вы не можете полагаться на то, что другие поля уже созданы с использованием этого подхода
4. Я боролся с этим и часто использовал подобные классы, но в последнее время я останавливаюсь либо на сеттерах, которые возвращают
this
вместоvoid
, чтобы вы могли связать их в цепочку, либо на шаблоне Builder. С цепочкой вы могли бы сделать Eat(новый человек («Джон», 30).setX(12.5).setY (15.3));
Ответ №1:
Да, он будет (всегда) генерировать анонимный класс (называемый Human $ 1). Вы можете убедиться в этом, изучив файлы классов, которые выводятся. У вас должен быть Human.class и человек $1.class в качестве вывода.
Что касается последствий для производительности, будет два класса (больше, больше работы для виртуальной машины), ссылки от одного к другому (потому что анонимный внутренний класс будет иметь ссылку на внешний класс). Я полагаю, это может повлиять на производительность, но незначительно. Вам придется это протестировать.
Однако, это не особенно идиоматично Java, чтобы делать это таким образом. Идиоматическим способом было бы иметь другой конструктор.
Ответ №2:
Компилятор создаст отдельный двоичный класс.
Например, если у вас есть
class Foo{
}
class Bar{
Foo otherFoo = new Foo(){
}
}
В вашем каталоге bin / target у вас будет три класса
- Bar.class
- Bar $1.class
- Foo.class
Здесь анонимный подкласс Bar$1.class