Получить доступ к this перед вызовом super в конструкторе
От: vsb Казахстан  
Дата: 18.07.19 09:09
Оценка:
Появилась необходимость получить доступ к this перед вызовом super в конструкторе.

Имеется класс AutoCompleteTextField с конструктором

public AutoCompleteTextField(final String id, final IModel<T> model, final Class<T> type,
    final IAutoCompleteRenderer<T> renderer, final AutoCompleteSettings settings)


Я создаю наследника этого класса: DictAutoCompleteTextField

    private DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model) {
        super(id, model, Dict.class, new AutoCompleteRenderer(), newAutoCompleteSettings());


Мне нужно передать в параметр rendered свою реализацию AutoCompleteRenderer, причём этой реализации нужен доступ к самому наследнику DictAutoCompleteTextField.

Возможности вызвать что-то вроде AutoCompleteTextFieldюsetRenderer нет, поле AutoCompleteTextFieldюrenderer final и инициализируется в конструкторе. Класс AutoCompleteTextField из фреймворка и менять я его тоже не могу. Функции вроде AutoCompleteTextField.getRenderer тоже нет, чтобы вызвать её и инициализировать мой Renderer после вызова super.

Единственное, что приходит в голову — завести статическую ThreadLocal переменную, присваивать ей AutoCompleteRenderer в его конструкторе и доставать его сразу после вызова super. Какой-то костыль. Ну или reflection-ом достать.

Можно ли придумать какой-нибудь синтаксичекий трюк или что-то в этом роде? Самое логичное для меня это код вроде

    private DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model) {
        AutoCompleteRenderer renderer = new AutoCompleteRenderer();
        super(id, model, Dict.class, renderer, newAutoCompleteSettings());
        renderer.setOwner(this);


Но по какому-то глупому ограничению я не то, что получить доступ к this до вызова super не могу, я вообще ничего там делать не могу.
Отредактировано 18.07.2019 9:18 vsb . Предыдущая версия . Еще …
Отредактировано 18.07.2019 9:18 vsb . Предыдущая версия .
Re: Получить доступ к this перед вызовом super в конструктор
От: · Великобритания  
Дата: 18.07.19 09:53
Оценка: 19 (2) +1
Здравствуйте, vsb, Вы писали:

vsb>Появилась необходимость получить доступ к this перед вызовом super в конструкторе.

Ну, понятно, нельзя. this ещё как бы не существует до вызова super, т.к. не сконструирован и вообще в хз каком состоянии.
Ты пытаешься сделать циклическую зависимость между двумя конструкторами — для конструирования X, нужен Y, а чтобы сконструировать Y нужен X.
Ну... э... не делай так. Надо:
— подумать хорошенько с какого это перепугу renderer вообще должен зависеть от поля которое он рендерит?
— ввести третий класс Z от которого X и Y будут зависеть.
DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model, Zzz zzz) {
    super(id, model, Dict.class, new AutoCompleteRenderer(zzz), newAutoCompleteSettings());
    this.zzz = zzz;
}
// потом можно и ещё конструктор добавить:
DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model) {
    this(id, dictName, model, new Zzz());
}
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 18.07.2019 9:53 · . Предыдущая версия .
Re: Получить доступ к this перед вызовом super в конструкторе
От: StanislavK Великобритания  
Дата: 18.07.19 11:41
Оценка: 15 (1)
Здравствуйте, vsb, Вы писали:

vsb>Можно ли придумать какой-нибудь синтаксичекий трюк или что-то в этом роде? Самое логичное для меня это код вроде


vsb>
vsb>    private DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model) {
vsb>        AutoCompleteRenderer renderer = new AutoCompleteRenderer();
vsb>        super(id, model, Dict.class, renderer, newAutoCompleteSettings());
vsb>        renderer.setOwner(this);
vsb>


vsb>Но по какому-то глупому ограничению я не то, что получить доступ к this до вызова super не могу, я вообще ничего там делать не могу.


Страшно, конечно, но можно так попробовать:

сlass Foo {
    Renderer renderer;
    Foo(Renderer renderer) {
        this.renderer = renderer;
    }
}

class Bar extends Foo {
    Bar() {
        this(new BarRenderer());
    }

    Bar(BarRenderer renderer) {
        super(renderer);
        renderer.bar = this;
    }

    void barRender() {

    }

    static class BarRenderer implements Renderer {
        Bar bar;
        @Override
        public void render() {
            bar.barRender();
        }
    }
}

interface Renderer {
    void render();
}
Re: Получить доступ к this перед вызовом super в конструкторе
От: kov_serg Россия  
Дата: 18.07.19 14:29
Оценка: 10 (1)
Здравствуйте, vsb, Вы писали:

vsb>Мне нужно передать в параметр rendered свою реализацию AutoCompleteRenderer, причём

vsb>этой реализации нужен доступ к самому наследнику DictAutoCompleteTextField.
Зачем так делать?

vsb>
vsb>    private DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model) {
vsb>        AutoCompleteRenderer renderer = new AutoCompleteRenderer();
vsb>        super(id, model, Dict.class, renderer, newAutoCompleteSettings());
vsb>        renderer.setOwner(this);
vsb>


vsb>Но по какому-то глупому ограничению я не то, что получить доступ к this до вызова super не могу, я вообще ничего там делать не могу.

Что мешает сделать отдельный метод для создания экземпляра. Типа такого:
public static DictAutoCompleteTextField create(...) {
  AutoCompleteRenderer renderer = new AutoCompleteRenderer();
  DictAutoCompleteTextField result = new DictAutoCompleteTextField(...,renderer);
  renderer.setOwner(result);
  return result;
}
Re[2]: Получить доступ к this перед вызовом super в конструктор
От: vsb Казахстан  
Дата: 18.07.19 19:50
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, vsb, Вы писали:


vsb>>Появилась необходимость получить доступ к this перед вызовом super в конструкторе.

·>Ну, понятно, нельзя. this ещё как бы не существует до вызова super, т.к. не сконструирован и вообще в хз каком состоянии.
·>Ты пытаешься сделать циклическую зависимость между двумя конструкторами — для конструирования X, нужен Y, а чтобы сконструировать Y нужен X.
·>Ну... э... не делай так. Надо:
·>- подумать хорошенько с какого это перепугу renderer вообще должен зависеть от поля которое он рендерит?

Нада Конкретно — локаль получить, в которой надо выводить. А локаль по уму достаётся из компонента. По-хорошему, конечно, она должна бы передаваться параметром, но не передаётся.
Re[2]: Получить доступ к this перед вызовом super в конструкторе
От: vsb Казахстан  
Дата: 18.07.19 20:04
Оценка:
Здравствуйте, StanislavK, Вы писали:

SK>Здравствуйте, vsb, Вы писали:


SK>Страшно, конечно, но можно так попробовать:


Создать промежуточный класс? Да, пожалуй так будет чище всего, спасибо.
Re[2]: Получить доступ к this перед вызовом super в конструкторе
От: vsb Казахстан  
Дата: 18.07.19 20:05
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Что мешает сделать отдельный метод для создания экземпляра. Типа такого:


Ну можно и так, но тогда надо для каждого наследника что-то подобное писать, да и общий стиль нарушается, все компоненты через new создаются.
Re[3]: Получить доступ к this перед вызовом super в конструктор
От: · Великобритания  
Дата: 18.07.19 21:28
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>·>- подумать хорошенько с какого это перепугу renderer вообще должен зависеть от поля которое он рендерит?


vsb>Нада Конкретно — локаль получить, в которой надо выводить. А локаль по уму достаётся из компонента. По-хорошему, конечно, она должна бы передаваться параметром, но не передаётся.

Не нада. Renderer не должен ничего о локали знать. Он нужен только для обрамления значения T в кусок html. А локализованное значение уже надо держать в самом T.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Получить доступ к this перед вызовом super в конструк
От: vsb Казахстан  
Дата: 18.07.19 22:16
Оценка:
Здравствуйте, ·, Вы писали:

vsb>>·>- подумать хорошенько с какого это перепугу renderer вообще должен зависеть от поля которое он рендерит?


vsb>>Нада Конкретно — локаль получить, в которой надо выводить. А локаль по уму достаётся из компонента. По-хорошему, конечно, она должна бы передаваться параметром, но не передаётся.

·>Не нада. Renderer не должен ничего о локали знать. Он нужен только для обрамления значения T в кусок html. А локализованное значение уже надо держать в самом T.

У меня в T держатся значения для всех (двух) локалей. Простой пример без специфики, по-твоему я не могу держать Integer в качестве T? Ведь чтобы число в строку преобразовать, мне нужна локаль (ну если по уму делать).
Отредактировано 18.07.2019 22:17 vsb . Предыдущая версия . Еще …
Отредактировано 18.07.2019 22:16 vsb . Предыдущая версия .
Re[3]: Получить доступ к this перед вызовом super в конструкторе
От: kov_serg Россия  
Дата: 18.07.19 23:35
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Ну можно и так, но тогда надо для каждого наследника что-то подобное писать, да и общий стиль нарушается, все компоненты через new создаются.

Сделайте фабрику
Re[3]: Получить доступ к this перед вызовом super в конструктор
От: stenkil  
Дата: 19.07.19 12:09
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Здравствуйте, ·, Вы писали:



vsb>Нада Конкретно — локаль получить, в которой надо выводить. А локаль по уму достаётся из компонента. По-хорошему, конечно, она должна бы передаваться параметром, но не передаётся.


У каждого компонента своя локаль ?
Re[5]: Получить доступ к this перед вызовом super в конструк
От: · Великобритания  
Дата: 19.07.19 12:49
Оценка: 5 (1)
Здравствуйте, vsb, Вы писали:

vsb>·>Не нада. Renderer не должен ничего о локали знать. Он нужен только для обрамления значения T в кусок html. А локализованное значение уже надо держать в самом T.

vsb>У меня в T держатся значения для всех (двух) локалей. Простой пример без специфики, по-твоему я не могу держать Integer в качестве T? Ведь чтобы число в строку преобразовать, мне нужна локаль (ну если по уму делать).
Эээ.. Не помню точно как там это всё делается. Вроде локаль-преобразования там делаются через IConverter. Пихать это в тип — по-моему как-то не очень ложится в остальную архитектуру, поэтому у тебя и сабж-проблема. Т.е. "T" уже не должен требовать локали для манипуляций.
Ещё как вариант — не использовать AutoCompleteTextField (уж очень он конкретно заточен под конкретный юзкейз), а написать свой класс компонента, используя AutoCompleteBehavior. Кода там довольно мало.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Получить доступ к this перед вызовом super в конструкторе
От: Pavel Dvorkin Россия  
Дата: 19.07.19 13:59
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Но по какому-то глупому ограничению я не то, что получить доступ к this до вызова super не могу, я вообще ничего там делать не могу.


Оно совсем не глупое. До вызова super не отработал конструктор базового класса. Если , например, поля производного класса определяются в конструкторе через поля базового, то нетрудно себе представить, чем это может закончиться.
Запрет на доступ к this на первый взгляд менее логичен, так как this — это ссылка на объект и она изменена быть не может внутри вызова super(). Так что взять ее вроде как безопасно, и я пока не вижу, к каким проблемам это могло бы привести. Но что ты сможешь сделать, взяв ее ? Присваивать ее полям нельзя — еще не сконструированы поля базового класса, так что см. выше. Передать параметру конструктора ? Чем это будет лучше, чем если сделать это после вызова super ? Завести локальную переменную, как в твоем гипотетическом примере внизу ? Это такой ящик Пандоры открыть, что мало никому не покажется. Кто мне в таком случае запретит передать в new AutoCompleteRenderer() какое-то поле базового класса ?
With best regards
Pavel Dvorkin
Re[4]: Получить доступ к this перед вызовом super в конструктор
От: vsb Казахстан  
Дата: 19.07.19 15:59
Оценка:
Здравствуйте, stenkil, Вы писали:

vsb>>Нада Конкретно — локаль получить, в которой надо выводить. А локаль по уму достаётся из компонента. По-хорошему, конечно, она должна бы передаваться параметром, но не передаётся.


S>У каждого компонента своя локаль ?


Теоретически да. Дефолтная реализация берёт локаль из сессии пользователя, но ничего не мешает её изменить. Уж не знаю, зачем это понадобилось.
Re[2]: Получить доступ к this перед вызовом super в конструкторе
От: vsb Казахстан  
Дата: 19.07.19 16:09
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

vsb>>Но по какому-то глупому ограничению я не то, что получить доступ к this до вызова super не могу, я вообще ничего там делать не могу.


PD>Оно совсем не глупое. До вызова super не отработал конструктор базового класса. Если , например, поля производного класса определяются в конструкторе через поля базового, то нетрудно себе представить, чем это может закончиться.


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

На самом деле самое глупое то, что я вообще не могу никакой код написать до вызова super, пусть он даже к объекту класса пока не имеет никакого отношения. Вот этому объяснения я не вижу и не удивлюсь, если в байткоде такого ограничения нет. Тем более, что по факту любое выражение, переданное параметром в super, таки вычисляется до вызова super.

PD>Запрет на доступ к this на первый взгляд менее логичен, так как this — это ссылка на объект и она изменена быть не может внутри вызова super(). Так что взять ее вроде как безопасно, и я пока не вижу, к каким проблемам это могло бы привести. Но что ты сможешь сделать, взяв ее ? Присваивать ее полям нельзя — еще не сконструированы поля базового класса, так что см. выше.


Я могу передать this в new AutoCompleteRenderer(). Ну или просто сделать AutoCompleteRenderer не статическим вложенным классом, передав его неявно. Никаких проблем это вызвать не может, т.к. я его в конструкторе использовать не буду. Ну теоретически понятно, что мог бы, и увидел бы инициализированный нулями объект, плохо-плохо, понимаю.

PD>Передать параметру конструктора ? Чем это будет лучше, чем если сделать это после вызова super ? Завести локальную переменную, как в твоем гипотетическом примере внизу ? Это такой ящик Пандоры открыть, что мало никому не покажется. Кто мне в таком случае запретит передать в new AutoCompleteRenderer() какое-то поле базового класса ?


Ну я говорю, если уж так не хочется связываться с this, пускай там будет любой код, не трогающий this. Те же AutoCompleteSettings я вынужден зачем-то выносить в отдельный статический метод, хотя мог бы написать по-человечески.
Re[3]: Получить доступ к this перед вызовом super в конструкторе
От: Pavel Dvorkin Россия  
Дата: 20.07.19 01:44
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Я могу передать this в new AutoCompleteRenderer(). Ну или просто сделать AutoCompleteRenderer не статическим вложенным классом, передав его неявно. Никаких проблем это вызвать не может, т.к. я его в конструкторе использовать не буду. Ну теоретически понятно, что мог бы, и увидел бы инициализированный нулями объект, плохо-плохо, понимаю.


Вот именно. Передан был бы недоконструированный объект с нулевыми полями, и бог знает, что ты с этими полями будешь в new AutoCompleteRenderer делать.Да, передал бы ты туда только this, но дай тебе этот this — и ты там все , что в нем, получишь.

PD>>Передать параметру конструктора ? Чем это будет лучше, чем если сделать это после вызова super ? Завести локальную переменную, как в твоем гипотетическом примере внизу ? Это такой ящик Пандоры открыть, что мало никому не покажется. Кто мне в таком случае запретит передать в new AutoCompleteRenderer() какое-то поле базового класса ?


vsb>Ну я говорю, если уж так не хочется связываться с this, пускай там будет любой код, не трогающий this.


Не трогают this только локальные переменные и формальные параметры (ну и static, конечно). Вот int x = 10; написать там и впрямь было бы можно, и вполне безопасно. А все остальное так или иначе трогает this, в том числе и вложенный нестатический класс. Но было бы странным дать разрешение на введение до super кода, который не может обращаться к полям класса.
With best regards
Pavel Dvorkin
Re[4]: Получить доступ к this перед вызовом super в конструк
От: vsb Казахстан  
Дата: 20.07.19 10:59
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Но было бы странным дать разрешение на введение до super кода, который не может обращаться к полям класса.


И что же в этом странного? Это прекрасно решило бы мою проблему. Даже две проблемы. Вторая не такая существенная, но тоже неприятная. Видно её по названию newAutoCompleteSettings() под которым скрывается код инициализации настроек.

Т.е. код типа

DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model) {
    AutoCompleteSettings settings = new AutoCompleteSettings();
    settings.setSetting1("value1");
    super(settings);
}


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

Причём компилятор вполне умеет отделять такой код. Нельзя написать super(this.field), т.к. идёт обращение к this до вызова super.
Отредактировано 20.07.2019 11:00 vsb . Предыдущая версия .
Re[5]: Получить доступ к this перед вызовом super в конструк
От: Pavel Dvorkin Россия  
Дата: 21.07.19 13:38
Оценка:
Здравствуйте, vsb, Вы писали:


vsb>И что же в этом странного?



vsb>
vsb>DictAutoCompleteTextField(String id, String dictName, IModel<Dict> model) {
vsb>    AutoCompleteSettings settings = new AutoCompleteSettings();
vsb>    settings.setSetting1("value1");
vsb>    super(settings);
vsb>}
vsb>


В таком виде я проблем не вижу, но при условии, что созданный локальный объект используется только для передачи в super. При этом я не утверждаю, что проблемы нет, может, я просто ее не увидел.

Обосновать такое разрешение, похоже, можно (собственно, мы тут этим и занимались), но причины, по которым локальную переменную использовать можно, а к полям обратиться нельзя, не вполне очевидны, а поэтому введение такого разрешения/запрета делает язык менее ясным. Проще запретить все.
With best regards
Pavel Dvorkin
Re[3]: Получить доступ к this перед вызовом super в конструкторе
От: StanislavK Великобритания  
Дата: 22.07.19 12:05
Оценка:
Здравствуйте, vsb, Вы писали:

PD>>Оно совсем не глупое. До вызова super не отработал конструктор базового класса. Если , например, поля производного класса определяются в конструкторе через поля базового, то нетрудно себе представить, чем это может закончиться.


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


По теме ссылку увидел. Про конструкторы и их проблемы:

https://habr.com/ru/post/460831/
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.