Учитывая, что у меня есть класс Base, который имеет конструктор с одним аргументом с объектом TextBox в качестве аргумента. Если у меня есть класс Simple следующего вида:
public class Simple extends Base {
public Simple(){
TextBox t = new TextBox();
super(t);
//wouldn't it be nice if I could do things with t down here?
}
}
Я получаю сообщение об ошибке, сообщающее мне, что вызов super должен быть первым вызовом в конструкторе. Однако, как ни странно, я могу это сделать.
public class Simple extends Base {
public Simple(){
super(new TextBox());
}
}
Почему это разрешено, а в первом примере - нет? Я могу понять, что сначала нужно настроить подкласс и, возможно, не разрешать создание экземпляров объектных переменных до вызова суперконструктора. Но t явно является переменной метода (локальной), так почему бы не разрешить это?
Есть ли способ обойти это ограничение? Есть ли хороший и безопасный способ хранить переменные для вещей, которые вы могли бы построить ДО вызова super, но ПОСЛЕ того, как вы вошли в конструктор? Или, в более общем смысле, разрешить выполнение вычислений до фактического вызова super, но в конструкторе?
Спасибо.
Ответы:
Да, для вашего простого случая есть обходной путь. Вы можете создать частный конструктор, который принимает
TextBox
в качестве аргумента и вызывать его из вашего общедоступного конструктора.public class Simple extends Base { private Simple(TextBox t) { super(t); // continue doing stuff with t here } public Simple() { this(new TextBox()); } }
Для более сложных вещей вам нужно использовать фабричный или статический фабричный метод.
источник
super()
не появляется после чего-либо еще в конструкторе. В крайних случаях вы даже можете связать несколько частных конструкторов вместе, хотя я не могу представить себе ситуацию, в которой мне нужно было бы это сделать, и если бы я это сделал, я бы, вероятно, начал задаваться вопросом, есть ли лучший способ выполнить то, что я делал.super(this.getClass())
, но, поскольку вам не разрешено ссылатьсяthis
, этот код был незаконным, хотя кажется вполне разумным, что я должен знать реальный класс на этом этапе.getClass()
метода как особого случая в JLS.<classname>.class
Вместо этого используйте .<classname>.class
этом случае использование не было вариантом, потому что я писал абстрактный класс, который люди могли расширять, и мне нужен был фактический класс экземпляра.У меня была такая же проблема с вычислениями до супервызова. Иногда вы хотите проверить некоторые условия перед звонком
super()
. Например, у вас есть класс, который при создании использует много ресурсов. подклассу нужны дополнительные данные, и он может сначала проверить их, прежде чем вызывать суперконструктор. Есть простой способ обойти эту проблему. может показаться немного странным, но работает хорошо:Используйте частный статический метод внутри вашего класса, который возвращает аргумент суперконструктора и выполняет ваши проверки внутри:
public class Simple extends Base { public Simple(){ super(createTextBox()); } private static TextBox createTextBox() { TextBox t = new TextBox(); t.doSomething(); // ... or more return t; } }
источник
Это требуется языком, чтобы гарантировать, что суперкласс будет надежно построен первым . В частности, «Если конструктор не вызывает явно конструктор суперкласса, компилятор Java автоматически вставляет вызов конструктору суперкласса без аргументов».
В вашем примере суперкласс может полагаться на состояние
t
во время строительства. Вы всегда можете попросить копию позже.Там есть обширное обсуждение здесь и здесь .
источник
Вы можете определить статическую лямбду поставщика, которая может содержать более сложную логику.
public class MyClass { private static Supplier<MyType> myTypeSupplier = () -> { return new MyType(); }; public MyClass() { super(clientConfig, myTypeSupplier.get()); } }
источник
Причина, по которой второй пример разрешен, но не первый, скорее всего, поддерживает порядок в языке и не вводит странные правила.
Разрешить запуск любого кода до вызова super было бы опасно, так как вы можете испортить вещи, которые должны были быть инициализированы, но еще не были. В принципе, я предполагаю, что вы можете делать довольно много вещей при вызове самого super (например, вызывать статический метод для вычисления некоторых вещей, которые должны перейти в конструктор), но вы никогда не сможете использовать что-либо из не -пока-полностью построенный объект, что хорошо.
источник
Так работает Java :-) Есть технические причины, по которым она была выбрана именно так. Это действительно может быть странно, что вы не можете выполнять вычисления на локальных жителях перед вызовом super, но в Java объект должен быть сначала выделен, и, следовательно, он должен пройти весь путь до объекта, чтобы все поля были правильно инициализированы, прежде чем вы сможете случайно изменить их.
В вашем случае в большинстве случаев есть геттер, который позволяет вам получить доступ к параметру, который вы передали super (). Итак, вы бы использовали это:
источник
final
метод, который переопределяет подкласс. В этом случае переопределение не будет видеть инициализированные поля из подкласса (дажеfinal
те, которые имеют инициализаторы экземпляра!). Некоторые инструменты статического анализа справедливо предупредят об этой ситуации.Это мое решение, которое позволяет создавать дополнительный объект, изменять его без создания дополнительных классов, полей, методов и т. Д.
class TextBox { } class Base { public Base(TextBox textBox) { } } public class Simple extends Base { public Simple() { super(((Supplier<TextBox>) () -> { var textBox = new TextBox(); //some logic with text box return textBox; }).get()); } }
источник