Thursday, April 26, 2012

Порядок с инициализации полей в Java

Сегодня обнаружил интересную багу. Не сразу понял в чем проблема, пока не запустил дебаггер :)

Есть такой класс А

Есть вот такой наследник класса A: Есть еще один наследник класса А:

Внимание, вопрос:
Какой результат выведется на экран, если запустить метод A.main?

Правильный ответ (для JDK 6):
null
foo2

Интересный задротский вопрос для собеседования получился, включу в собеседование на сеньорную позицию в Java :)

3 comments:

  1. А знаешь почему так случается? При компиляции final полей с изначально установленным значением происходит вставка значения во все места в коде, где используется поле. Если ты его не сможешь поменять, то почему бы не прооптимизировать. После компиляции происходит так

    public class AChild2 extends A {

        public AChild2() {
        }

        protected String getFoo() {
            return "foo1";
        }

        private final String foo = "foo1";
    }


    Тогда как во втором классе, инициализация перенесется в конструктор, который выполнится сразу после отработки конструктора суперкласса (который запрашивает поле, но пока еще со значением null).

    public class AChild1 extends A {

        public AChild1() {
            foo = "foo1";
        }

        protected String getFoo() {
            return foo;
        }

        private String foo;
    }


    Нахимичили. Сколько еще таких подводных камней.

    Только вот я с tdd перестал париться по поводу чего-то типа этого примера. Мне обычно тесты говорят, что я что-то упустил. Тут же включается любознательность и открываю новое. Потом уже знаю, и могу поумничать на собеседовании. Вопрос в том, Сережа, что бы ты ответил на такое на собеседовании?

    ReplyDelete
    Replies
    1. Спасибо, Саш за разьяснение :)

      Давно я не ходил на собеседования. Хотя я иногда задаю подобные вопросы на синьорских интервью. Но я оцениваю не правильный ответ, а ход мысли человека и его возможность включить мозг в экстренной ситуации. Понятно, что мало кто знает детали оптимизации кода, но иногда имея базовые понятия можно дойти до правильного ответа самому.

      Так что я бы, скорее всего, попытался максимально включить логику и излагал бы ход своих мыслей интервьюеру. Дошел бы до правильного ответа или нет - не знаю, но если бы я не прошел интервью из-за того, что не дал правильного ответа в этом вопросе, то не огорчился бы. С задротами, которые требуют доскональных ответов на вопросы типа "чем отличается WeakReference от SoftReference" или "в какой версии Servlet спецификации появились фильтры" иногда бывает неинтересно работать :).

      Delete
    2. Серега, и очень здорово что так. Тогда правильный ответ - jad :) Я им пользуюсь регулярно, когда какая-то митстика появляется. А когда вижу что по факту с кодом произошло после компиляции, то ищу описание почему это так в документации.

      Но перед jad я бы написал пачку тестов на интеерсный кейс, который вызывает у меня сомнения, причем кейс акуратно выкристализовал бы из легаси кода, с которым работал в такой же примерчик как у тебя.

      Ну и в блог естественно! Если причерчик очень интересный.

      Часто, такие интересные кейсы как грибы, очень прячутся под листвой, а потому только что-то идет не так как я себе запланировал с кодом - верный признак, надо копнуть - где-то я чего-то не знаю.

      Только вот когда я так начинаю рассуждать на собеседовании, то на меня как-то странно косятся. В глазах читается "это надо знать!". Им почему-то пофиг, что подобные алгоритмы работают везде, и не важно j2ee это или php, или разворачивания moodle на бесплатном хостинге или что-то еще новое.

      Delete