Можно ли вызвать конструктор из другого (в том же классе, а не из подкласса)? Если да, то как? И как лучше всего вызвать другой конструктор (если есть несколько способов сделать это)?
Я обычно перешел на частные конструкторы и фабричные методы, поскольку конструкторы из-за своих ограничений нарушают принцип открытого-закрытого. Я думаю, что этот комментарий должен быть правильным ответом, все остальное запутает ваших товарищей по команде.
Срой, но это не очень хорошая практика, если вы хотите сделать что-то подобное, перезарядите конструктор. Если вы хотите обернуть контент, это можно сделать, но для другого файла pourpuse. Не конструктор открытого класса Foo {private int x; public Foo () {} public Foo (int x) {this.x = x; } public Foo (int x, int y) {this.x = x; this.y = y}
Вызов конструктора из другого конструктора в Java - это в первую очередь средство предоставления значений по умолчанию для параметров одному конструктору, который должен фактически создавать ваш объект, а затем достаточно просто присвоить значения в теле конструктора. Если ваш объект требует сложной конструкции, это запах кода, указывающий на то, что вашему классу не хватает сплоченности. Если вам недостаточно конструктора, вы, вероятно, плохо поработали над проектированием своих классов, что вы обнаружите, когда придет время вносить изменения в будущем.




Да, это возможно:
public class Foo {
private int x;
public Foo() {
this(1);
}
public Foo(int x) {
this.x = x;
}
}
Чтобы привязать к конкретному конструктору суперкласса вместо конструктора того же класса, используйте super вместо this. Обратите внимание, что вы можете привязать только к одному конструктору и это должен быть первый оператор в теле вашего конструктора.
См. Также этот связанный вопрос, посвященный C#, но в котором применяются те же принципы.
Итак, я предположил, что невозможно вызвать суперконструктор и другой конструктор того же класса, поскольку оба должны быть первой строкой?
@ gsingh2011: Верно. Вы можете подключиться только к другому конструктору один.
Это должно появиться в первой строке, но вы можете выполнять вычисления в конструкторе до его вызова: вы можете использовать статические методы в аргументах this () в первой строке и инкапсулировать любые вычисления, которые должны быть выполнены перед вызовом другому конструктору в этом статическом методе. (Я добавил это как отдельный ответ).
@ gsingh2011 Я знаю, что уже поздно, но вы можете вызвать перегруженный конструктор, используя this (...), а затем в этом перегруженном конструкторе вы можете вызвать конструктор базового класса, используя super (...)
@JonSkeet Да! мы можем связать с одним другим конструктором, но на самом деле у нас может быть очень длинная цепочка
@Mohit: И почему это проблема? На самом деле непонятно, в чем заключается контекст вашего комментария ...
нет, не проблема, я только что добавил к вашему комментарию, что "только еще один конструктор", поэтому многие другие создают длинную цепочку
В обязательном порядке, если вы используете this () или super (), они должны быть первым оператором конструктора, и оба они не могут использоваться в конструкторе.
@ gsingh2011 да, по какой-то причине разрешен только один конструктор класса.
@Francis: Я предполагаю, что вы имеете в виду «вы можете связать только один другой конструктор» - в настоящее время ваше утверждение звучит так, как будто вы можете имеют только один конструктор на класс, что явно неверно.
@JonSkeet да, я имел в виду цепочку только с одним другим конструктором. спасибо за исправление, и я прошу прощения за мое вводящее в заблуждение заявление.
@Francis Я не уверен, но я считаю, что причина в том, что если первая строка конструктора делегирует другому конструктору или вызывает конструктор суперкласса, он фактически запускает до, объект создается. C++ имеет такое же ограничение, у вас может быть только один вызов конструктора родительского класса или (как в C++ 11) одно делегирование для каждого конструктора, и не более того, причем оба они выполняются до создания объекта.
@JustinTime: Вам нужно определить, что вы имеете в виду под «до того, как объект будет построен», чтобы я мог судить об этом ...
@JonSkeet Я имею в виду, что исходя из того, что я знаю о Java, вызовы конструктора делегирования или суперкласса выполняются до инициализации полей объекта и переводятся в состояние, в котором он считается «созданным». В частности, я считаю, что процесс: 1) Запуск конструктора родительского класса / суперкласса для инициализации унаследованной части класса, 2) Инициализация переменных-членов, зависящих от экземпляра, чтобы объект был действительным, чтобы доступ к члену в теле конструктора ничего не сломал. , 3) Выполнить тело конструктора, чтобы завершить построение. Он считается построенным к концу шага 3.
В этом отношении я считаю, что он работает так же, как C++ (построить унаследованную часть, инициализировать объект из списка инициализации, запустить тело конструктора), но с более чистым, но менее конкретным синтаксисом (список инициализации перемещается в верхнюю часть тело конструктора вместо того, чтобы быть после подписи, но перед телом), с делегированием, передающим задание фактического создание объекта другому конструктору, а затем запускает любой код в делегирующем конструкторе на допустимом объекте после конструктора, которому он делегирует возвращается. Поправьте меня если я ошибаюсь.
Создавая объект, я имею в виду выделение памяти, установку типа и инициализацию полей, чтобы объект находился в допустимом, пригодном для использования состоянии (неявный первый шаг каждого конструктора), с делегированием, передающим задачу сделать объект действительным и можно использовать в конструкторе, которому делегирован. Не знал, что объекты не запускаются как их фактический тип в C++, но имеет смысл рассмотреть, как C++ неявно связывает конструктор (ы) родительского класса для создания унаследованной части производного класса перед фактическим выполнением конструктора производного класса; Думаю, мне придется этого опасаться.
Я знаю, что опаздываю в раздел комментариев, но не понимаю, почему new Foo(1); не работает?
@Andrej: Это действительно было бы «вызовом одного конструктора из другого», но он не будет делать то, что хочет OP, а именно инициализировать один объект с помощью нескольких конструкторов, один соединяется с другим. Создание двух объектов путем простого создания отдельного объекта в одном вызове конструктора - это совсем не одно и то же.
Что за ублюдок это придумал? Тысячи людей задаются вопросом.
@mrhotroad: Пожалуйста, пишите на этом сайте по-английски.
Используя this(args). Предпочтительный шаблон - работать от самого маленького конструктора до самого большого.
public class Cons {
public Cons() {
// A no arguments constructor that sends default values to the largest
this(madeUpArg1Value,madeUpArg2Value,madeUpArg3Value);
}
public Cons(int arg1, int arg2) {
// An example of a partial constructor that uses the passed in arguments
// and sends a hidden default value to the largest
this(arg1,arg2, madeUpArg3Value);
}
// Largest constructor that does the work
public Cons(int arg1, int arg2, int arg3) {
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
}
}
Вы также можете использовать недавно отстаиваемый подход valueOf или просто «of»:
public class Cons {
public static Cons newCons(int arg1,...) {
// This function is commonly called valueOf, like Integer.valueOf(..)
// More recently called "of", like EnumSet.of(..)
Cons c = new Cons(...);
c.setArg1(....);
return c;
}
}
Для вызова суперкласса используйте super(someValue). Вызов super должен быть первым вызовом в конструкторе, иначе вы получите ошибку компилятора.
Если используется много параметров конструктора, подумайте о строителе. См. Пункт 2 книги Джошуа Блоха «Эффективная Java».
Проблема с реализацией последнего подхода с использованием фабричного метода, newCons, заключается в том, что вы пытаетесь изменить состояние объекта с помощью setArg1(...), поля которого, скорее всего, должны быть установлены как final. Поскольку мы пытаемся сохранить как можно большую часть объекта неизменным, если не полностью, шаблон построителя решит эту проблему более правильно.
Вы бы предпочли: public Cons () {this (madeUpArg1Value, madeUpArg2Value); }
@ RodneyP.Barbati В Java конструкторы с меньшей арностью часто вызывают конструкторы с большей арностью а потом больше ничего не делай. если класс K имеет, например, два последних поля a, b, тогда «общий конструктор» будет K(A a, B b) { this.a = a; this.b = b; }. Затем, если b имеет разумное значение по умолчанию, может быть конструктор с одним аргументом K(A a) { this(a, DEFAULT_B); }, а если есть также a по умолчанию, у нас есть конструктор по умолчанию: K() { this(DEFAULT_A); }. Это довольно распространенное соглашение в Java.
@ RodneyP.Barbati Если у вас есть последнее поле (так что оно должно быть установлено), то конструктор по умолчанию должен будет установить его. Если ваши конструкторы с более высокой степенью арности вызывают конструктор по умолчанию (что должно быть сделано раньше, чем что-либо еще), то конструкторы с более высокой степенью арности никогда не имеют никаких параметров для установки любого из этих полей.
Этот работает с Android Studio, а принятый ответ - нет. Не знаю почему.
[Примечание: я просто хочу добавить один аспект, который я не видел в других ответах: как преодолеть ограничения требования, чтобы this () был в первой строке).]
В Java другой конструктор того же класса может быть вызван из конструктора через this(). Однако обратите внимание, что this должен быть в первой строке.
public class MyClass {
public MyClass(double argument1, double argument2) {
this(argument1, argument2, 0.0);
}
public MyClass(double argument1, double argument2, double argument3) {
this.argument1 = argument1;
this.argument2 = argument2;
this.argument3 = argument3;
}
}
То, что this должен появиться в первой строке, выглядит большим ограничением, но вы можете создавать аргументы других конструкторов с помощью статических методов. Например:
public class MyClass {
public MyClass(double argument1, double argument2) {
this(argument1, argument2, getDefaultArg3(argument1, argument2));
}
public MyClass(double argument1, double argument2, double argument3) {
this.argument1 = argument1;
this.argument2 = argument2;
this.argument3 = argument3;
}
private static double getDefaultArg3(double argument1, double argument2) {
double argument3 = 0;
// Calculate argument3 here if you like.
return argument3;
}
}
Это правда, что вы можете вызывать статические методы таким образом, чтобы выполнять сложные вычисления для значений аргументов, и это нормально. Однако, если кто-то чувствует, что код необходим перед делегированием конструктора (this(...)), тогда было бы разумно предположить, что где-то была сделана ужасная ошибка и что дизайн, возможно, нуждается в небольшом переосмыслении.
Я согласен с тем, что сложное преобразование очень, вероятно, указывает на проблему дизайна. Но 1) есть несколько простых преобразований, для которых это может быть полезно - не все конструкторы являются просто линейной проекцией на другие и 2) может быть другая ситуация, когда эта информация может стать ручной, например, поддержка устаревшего кода. (Хотя я согласен с вашим выводом, я не понимаю, почему это оправдывает голосование против).
@ RodneyP.Barbati: Я вижу несколько проблем в том, чтобы делать это так, как вы это описываете: a) Делая это таким образом, невозможно проиллюстрировать использование статического метода в конструкторе (и это цель примера); -) и б) если вы делаете это по-своему, поля не могут быть final (конечные поля могут быть инициализированы только один раз).
@ RodneyP.Barbati: Два других аспекта: c) Я считаю, что вы всегда должны выполнять инициализацию объекта в одной точке, которая должна быть самым общим конструктором. Если для инициализации объекта требуется сложная задача (инициализация объекта не является ленивой) или проверка или получение некоторых ресурсов (например, файла), то вам нравится делать это только один раз. И г) Добавление еще одного аргумента (например, аргумента 4), для которого инициализация зависит от значения аргумента 1 до аргумента 3, вам придется изменить все конструкторы в вашем случае, тогда как здесь вам нужно только добавить один и позволить 3-arg вызвать 4 -arg конструктор.
Для более общего метода преодоления ограничения «оператор должен быть первым в конструкторе» см. этот ответ. Это применимо как для вызовов super(), так и для вызовов this().
Когда мне нужно вызвать другой конструктор изнутри кода (не в первой строке), я обычно использую вспомогательный метод вроде этого:
class MyClass {
int field;
MyClass() {
init(0);
}
MyClass(int value) {
if (value<0) {
init(0);
}
else {
init(value);
}
}
void init(int x) {
field = x;
}
}
Но чаще всего я пытаюсь сделать наоборот, вызывая более сложные конструкторы из более простых в первой строке, насколько это возможно. Для приведенного выше примера
class MyClass {
int field;
MyClass(int value) {
if (value<0)
field = 0;
else
field = value;
}
MyClass() {
this(0);
}
}
Как все уже сказали, вы используете this(…), который называется явный вызов конструктора.
Однако имейте в виду, что внутри такого явного оператора вызова конструктора вы не можете ссылаться на
this илиsuper.Как указано в JLS (§8.8.7.1).
В конструкторе вы можете использовать ключевое слово this для вызова другого конструктора в том же классе. Это называется явный вызов конструктора.
Вот еще один класс Rectangle, реализация которого отличается от реализации в разделе «Объекты».
public class Rectangle {
private int x, y;
private int width, height;
public Rectangle() {
this(1, 1);
}
public Rectangle(int width, int height) {
this( 0,0,width, height);
}
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
Этот класс содержит набор конструкторов. Каждый конструктор инициализирует некоторые или все переменные-члены прямоугольника.
почему бы вам не вызвать второй конструктор, который является Rectangle(int width, int height) в Rectangle() вместо Rectangle(int x, int y, int width, int height)?
@ RodneyP.Barbati Я не могу согласиться с этим. Этот шаблон не допускает полей final.
Вы можете создать конструктор из другого конструктора того же класса, используя ключевое слово this. Пример -
class This1
{
This1()
{
this("Hello");
System.out.println("Default constructor..");
}
This1(int a)
{
this();
System.out.println("int as arg constructor..");
}
This1(String s)
{
System.out.println("string as arg constructor..");
}
public static void main(String args[])
{
new This1(100);
}
}
Выход - строка как конструктор arg .. Конструктор по умолчанию .. int как конструктор arg ..
Да, можно вызывать один конструктор из другого с использованием this()
class Example{
private int a = 1;
Example(){
this(5); //here another constructor called based on constructor argument
System.out.println("number a is "+a);
}
Example(int b){
System.out.println("number b is "+b);
}
Да, в классе может присутствовать любое количество конструкторов, и они могут быть вызваны другим конструктором с использованием this() [Пожалуйста, не путайте вызов конструктора this() с ключевым словом this]. this() или this(args) должны быть первой строкой в конструкторе.
Пример:
Class Test {
Test() {
this(10); // calls the constructor with integer args, Test(int a)
}
Test(int a) {
this(10.5); // call the constructor with double arg, Test(double a)
}
Test(double a) {
System.out.println("I am a double arg constructor");
}
}
Это называется перегрузкой конструктора. Обратите внимание, что для конструктора применима только концепция перегрузки, а не наследование или переопределение.
Я расскажу вам простой способ
Есть конструкторы типа два:
Я объясню на одном примере
class ConstructorDemo
{
ConstructorDemo()//Default Constructor
{
System.out.println("D.constructor ");
}
ConstructorDemo(int k)//Parameterized constructor
{
this();//-------------(1)
System.out.println("P.Constructor = "+k);
}
public static void main(String[] args)
{
//this(); error because "must be first statement in constructor
new ConstructorDemo();//-------(2)
ConstructorDemo g=new ConstructorDemo(3);---(3)
}
}
В приведенном выше примере я показал 3 типа звонков
Примечание: это должен быть первый оператор в конструкторе.
В основном методе у вас есть следующее: //это(); ошибка, потому что "должно быть первым оператором в конструкторе Этот оператор не имеет особого смысла. Если вы пытаетесь сказать, что это() не может быть вызван изнутри метода основной, тогда да, это не может быть, потому что main является статическим и не будет иметь ссылки на это()
Да, можно вызывать один конструктор из другого. Но есть правило. Если вызов осуществляется от одного конструктора к другому, то
that new constructor call must be the first statement in the current constructor
public class Product {
private int productId;
private String productName;
private double productPrice;
private String category;
public Product(int id, String name) {
this(id,name,1.0);
}
public Product(int id, String name, double price) {
this(id,name,price,"DEFAULT");
}
public Product(int id,String name,double price, String category){
this.productId=id;
this.productName=name;
this.productPrice=price;
this.category=category;
}
}
Таким образом, что-то подобное ниже не сработает.
public Product(int id, String name, double price) {
System.out.println("Calling constructor with price");
this(id,name,price,"DEFAULT");
}
Кроме того, в случае наследования, когда создается объект подкласса, сначала вызывается конструктор суперкласса.
public class SuperClass {
public SuperClass() {
System.out.println("Inside super class constructor");
}
}
public class SubClass extends SuperClass {
public SubClass () {
//Even if we do not add, Java adds the call to super class's constructor like
// super();
System.out.println("Inside sub class constructor");
}
}
Таким образом, в этом случае также сначала объявляется вызов другого конструктора перед любыми другими операторами.
Вызов конструктора из другого конструктора
class MyConstructorDemo extends ConstructorDemo
{
MyConstructorDemo()
{
this("calling another constructor");
}
MyConstructorDemo(String arg)
{
System.out.print("This is passed String by another constructor :"+arg);
}
}
Также вы можете вызвать родительский конструктор, используя вызов super()
Ключевое слово это может использоваться для вызова конструктора из конструктора, при написании нескольких конструкторов для класса бывают случаи, когда вы хотите вызвать один конструктор из другого, чтобы избежать дублирования кода.
Ниже приведена ссылка, по которой я объясняю другую тему о конструкторах и методах получения () и сеттерах (), и я использовал класс с двумя конструкторами. Надеюсь, объяснения и примеры вам помогут.
Существуют шаблоны проектирования, которые покрывают потребность в сложной конструкции - если это невозможно сделать кратко, создайте фабричный метод или фабричный класс.
С последней версией java и добавлением лямбда-выражений легко создать конструктор, который может принимать любой код инициализации, который вы пожелаете.
class LambdaInitedClass {
public LamdaInitedClass(Consumer<LambdaInitedClass> init) {
init.accept(this);
}
}
Назовите это ...
new LambdaInitedClass(l -> { // init l any way you want });
Довольно просто
public class SomeClass{
private int number;
private String someString;
public SomeClass(){
number = 0;
someString = new String();
}
public SomeClass(int number){
this(); //set the class to 0
this.setNumber(number);
}
public SomeClass(int number, String someString){
this(number); //call public SomeClass( int number )
this.setString(someString);
}
public void setNumber(int number){
this.number = number;
}
public void setString(String someString){
this.someString = someString;
}
//.... add some accessors
}
А теперь небольшой бонус:
public SomeOtherClass extends SomeClass {
public SomeOtherClass(int number, String someString){
super(number, someString); //calls public SomeClass(int number, String someString)
}
//.... Some other code.
}
Надеюсь это поможет.
Я знаю, что есть так много примеров этого вопроса, но то, что я нашел, я помещаю здесь, чтобы поделиться своей идеей. есть два способа связать конструктор. В том же классе вы можете использовать это ключевое слово. в наследовании вам нужно использовать ключевое слово super.
import java.util.*;
import java.lang.*;
class Test
{
public static void main(String args[])
{
Dog d = new Dog(); // Both Calling Same Constructor of Parent Class i.e. 0 args Constructor.
Dog cs = new Dog("Bite"); // Both Calling Same Constructor of Parent Class i.e. 0 args Constructor.
// You need to Explicitly tell the java compiler to use Argument constructor so you need to use "super" key word
System.out.println("------------------------------");
Cat c = new Cat();
Cat caty = new Cat("10");
System.out.println("------------------------------");
// Self s = new Self();
Self ss = new Self("self");
}
}
class Animal
{
String i;
public Animal()
{
i = "10";
System.out.println("Animal Constructor :" +i);
}
public Animal(String h)
{
i = "20";
System.out.println("Animal Constructor Habit :"+ i);
}
}
class Dog extends Animal
{
public Dog()
{
System.out.println("Dog Constructor");
}
public Dog(String h)
{
System.out.println("Dog Constructor with habit");
}
}
class Cat extends Animal
{
public Cat()
{
System.out.println("Cat Constructor");
}
public Cat(String i)
{
super(i); // Calling Super Class Paremetrize Constructor.
System.out.println("Cat Constructor with habit");
}
}
class Self
{
public Self()
{
System.out.println("Self Constructor");
}
public Self(String h)
{
this(); // Explicitly calling 0 args constructor.
System.out.println("Slef Constructor with value");
}
}
Это называется антипаттерном конструктора телескопирования или цепочкой конструкторов. Да, вы определенно можете это сделать. Я вижу много примеров выше и хочу добавить, сказав, что если вы знаете, что вам нужны только два или три конструктора, это может быть нормально. Но если вам нужно больше, попробуйте использовать другой шаблон проектирования, например шаблон Builder. Например:
public Omar(){};
public Omar(a){};
public Omar(a,b){};
public Omar(a,b,c){};
public Omar(a,b,c,d){};
...
Вам может понадобиться больше. В этом случае отличным решением будет паттерн-строитель. Вот статья, может быть полезно https://medium.com/@modestofiguereo/design-patterns-2-the-builder-pattern-and-the-telescoping-constructor-anti-pattern-60a33de7522e
Вы можете вызвать другой конструктор с помощью ключевого слова this(...) (когда вам нужно вызвать конструктор из того же класса) или ключевого слова super(...)
(когда вам нужно вызвать конструктор из суперкласса).
Однако такой вызов должен быть оператором первый вашего конструктора. Для превосходить этого ограничения используйте этот ответ.
Первоначально из ансера Мирко Клемма, немного измененного для ответа на вопрос:
Просто для полноты: существует также Блок инициализации экземпляра, который выполняется всегда и до вызова любого другого конструктора. Он состоит просто из блока операторов "{...}" где-то в теле определения вашего класса. У вас может быть даже больше одного. Вы не можете вызывать их, но они похожи на код «общего конструктора», если вы хотите повторно использовать некоторый код в конструкторах, аналогично вызываемым методам.
Так что в вашем случае
{
System.out.println("this is shared constructor code executed before the constructor");
field1 = 3;
}
Существует также "статическая" версия этого для инициализации статических членов: "static {...}"
Я предпочитаю такой способ:
class User {
private long id;
private String username;
private int imageRes;
public User() {
init(defaultID,defaultUsername,defaultRes);
}
public User(String username) {
init(defaultID,username, defaultRes());
}
public User(String username, int imageRes) {
init(defaultID,username, imageRes);
}
public User(long id, String username, int imageRes) {
init(id,username, imageRes);
}
private void init(long id, String username, int imageRes) {
this.id=id;
this.username = username;
this.imageRes = imageRes;
}
}
Да, вы можете вызывать конструкторы из другого конструктора. Например:
public class Animal {
private int animalType;
public Animal() {
this(1); //here this(1) internally make call to Animal(1);
}
public Animal(int animalType) {
this.animalType = animalType;
}
}
вы также можете прочитать подробности из Цепочка конструкторов в Java
Используя это ключевое слово, мы можем вызвать один конструктор в другом конструкторе в том же классе.
Пример :-
public class Example {
private String name;
public Example() {
this("Mahesh");
}
public Example(String name) {
this.name = name;
}
}
Да, это возможно
public class User {
private String name = "";
private String surname = "";
private int age = 0;
public User(){
this("name is undefined","surname is undefined",0);
}
public User(String name,String surname){
this(name,surname,0);
}
public User(String name, String surname, int age) {
this.name = name;
this.surname = surname;
this.age = age;
}
}
Я считаю, что посылка вашего вопроса неверна. Вместо вызова конструктора внутри конструктора используйте шаблон Factory. Статический фабричный метод сначала создает все объекты нижнего уровня. Затем он создает объекты более высокого уровня, которые получают результаты от вызова фабрики. Этот метод устраняет сложность модели, что способствует поддержанию, ясности и тестированию.