ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Java 中从另一个构造函数调用一个构造函数?

是否可以从另一个(在同一个类中,而不是从子类中)调用构造函数?如果是怎么办?调用另一个构造函数的最佳方法是什么(如果有几种方法可以做到)?

我相信你问题的前提是错误的。不要在构造函数中调用构造函数,而是使用工厂模式。静态工厂方法首先创建所有较低级别的对象。然后它构造从工厂调用返回的更高级别的对象。这种技术消除了模型的复杂性,有助于维护、清晰和测试。
我通常切换到私有构造函数和工厂方法,因为构造函数由于其局限性而违反了开闭原则。我认为这个评论应该是正确的答案,其他一切都会让你的队友感到困惑。
Sroy 但如果你想做这样的事情,这不是一个好习惯,对构造函数过度收费。如果你想包装一个内容,那可以做到,但需要另一个 pourpuse。不是构造函数 public class Foo { private int x;公共 Foo() { } 公共 Foo(int x) { this.x = x; } public Foo(int x, int y) { this.x = x;这个.y = y }
从 Java 中的另一个构造函数调用构造函数主要是一种为实际构造对象的构造函数提供参数默认值的方法,然后只需在构造函数的主体中分配值就足够了。如果您的对象需要复杂的构造,那是一种代码味道,表明您的类缺乏凝聚力。如果构造函数对您来说还不够,那么您可能在设计类方面做得很差,您会在以后需要进行更改时发现这些工作。
为什么我们不能只使用 new 而不是使用“this”,例如在无参数构造函数中调用 new Foo(5);这个对吗?如果不是为什么会这样? @彼得

C
Community

对的,这是可能的:

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

    public Foo(int x) {
        this.x = x;
    }
}

要链接到特定的超类构造函数而不是同一类中的构造函数,请使用 super 而不是 this。请注意,您只能链接到一个构造函数,并且它必须是构造函数主体中的第一条语句

另请参阅 this related question,它是关于 C#,但适用相同的原则。


所以我认为不可能调用同一个类的超级构造函数和另一个构造函数,因为两者都需要成为第一行?
@gsingh2011:确实。您只能链接到另一个构造函数。
这必须出现在第一行,但您可以在调用之前在构造函数中进行计算:您可以在第一行的 this() 的参数中使用静态方法,并封装必须在调用之前执行的任何计算到该静态方法中的另一个构造函数。 (我已将此作为单独的答案添加)。
@gsingh2011 我知道已经晚了,但是作为一种解决方法,您可以使用 this(...) 调用重载构造函数,然后在该重载构造函数中,您可以使用 super(...) 调用基类的构造函数
@Andrej:这确实是“从另一个调用一个构造函数”,但它不会做OP想要的,即通过多个构造函数初始化一个对象,一个链接到另一个。通过在一个构造函数调用中创建一个单独的对象来创建两个对象根本不是一回事。
6
6 revs, 4 users 74%

使用 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 的调用必须是构造函数中的第一个调用,否则会出现编译器错误。


如果使用了许多构造函数参数,请考虑使用构建器。请参阅 Joshua Bloch 的“Effective Java”的第 2 条。
使用工厂方法 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 如果您有一个 final 字段(因此必须设置它),那么默认构造函数必须设置它。如果您的更高数量的构造函数调用默认构造函数(必须在其他任何事情之前完成),那么更高数量的构造函数永远没有任何选项来设置任何这些字段。
Z
Zizouz212

[注意:我只想添加一个方面,我在其他答案中没有看到:如何克服 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)这样做无法说明在构造函数中使用静态方法(这就是示例的意图); -) 和 b) 如果您按照自己的方式进行操作,则字段不能为 final(最终字段只能初始化一次)。
@RodneyP.Barbati:另外两个方面:c)我认为您应该始终在一个点进行对象初始化,这必须是最通用的构造函数。如果对象初始化需要复杂的任务(对象初始化不是懒惰的)或检查或获取一些资源(如文件),那么您喜欢只做一次。并且 d) 添加另一个参数(比如参数 4),其初始化取决于参数 1 到参数 3 的值,您必须更改您的情况下的所有构造函数,而在这里您只需添加一个并让 3-arg 调用 4 -arg 构造函数。
有关克服“必须是构造函数中的第一个语句”限制的更通用方法,请参阅 this answer。它适用于 super()this() 调用。
K
Kaamel

当我需要从代码内部(不是在第一行)调用另一个构造函数时,我通常使用这样的辅助方法:

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);
   }
}

a
amila isura

在构造函数中,您可以使用 this 关键字来调用同一类中的另一个构造函数。这样做称为显式构造函数调用

这是另一个 Rectangle 类,与 Objects 部分中的实现不同。

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() 中的 Rectangle(int width, int height) 而不是 Rectangle(int x, int y, int width, int height) 的第二个构造函数?
@RodneyP.Barbati 在这种情况下我不能同意。该模式不允许使用 final 字段。
o
olovb

正如大家已经说过的,您使用 this(…),这称为 显式构造函数调用

但是,请记住,在这样的显式构造函数调用语句中,您可能不会引用

任何实例变量或

任何实例方法或

在此类或任何超类中声明的任何内部类,或

这个或

极好的。

如 JLS (§8.8.7.1) 中所述。


U
Utsav

是的,一个类中可以存在任意数量的构造函数,并且它们可以由另一个构造函数使用 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");
    }
}

这称为构造函数重载。请注意,对于构造函数,仅适用于重载概念,不适用于继承或覆盖。


S
S R Chaitanya

是的,可以从另一个构造函数调用一个构造函数。但它有一个规则。如果从一个构造函数调用另一个构造函数,则

新的构造函数调用必须是当前构造函数中的第一条语句

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");
    }
}

因此,在这种情况下,另一个构造函数调用首先在任何其他语句之前声明。


A
Anil Nivargi

使用这个关键字,我们可以在同一个类的另一个构造函数中调用一个构造函数。

例子 :-

 public class Example {
   
      private String name;
   
      public Example() {
          this("Mahesh");
      }

      public Example(String name) {
          this.name = name;
      }

 }

S
Saikat

我告诉你一个简单的方法

有两种类型的构造函数:

默认构造函数参数化构造函数

我将在一个例子中解释

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 种调用类型

this() 对 this 的调用必须是构造函数中的第一条语句 This is Nameless Object。这会自动调用默认构造函数。 3.这会调用参数化构造函数。

注意:这必须是构造函数中的第一条语句。


您在 main 方法中有以下内容: //this();错误,因为“必须是构造函数中的第一条语句此语句没有多大意义。如果您想说 this() 不能从 main 方法内部调用,那么是的,它不能是因为 main 是静态的并且不会引用这个()
A
ABHISHEK RANA

您可以使用“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 构造函数..


J
J. Chomel

从另一个构造函数调用构造函数

class MyConstructorDemo extends ConstructorDemo
{
    MyConstructorDemo()
    {
        this("calling another constructor");
    }
    MyConstructorDemo(String arg)
    {
        System.out.print("This is passed String by another constructor :"+arg);
    }
}

您也可以使用 super() 调用来调用父构造函数


G
GetBackerZ

很简单

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.
}

希望这可以帮助。


R
Rodney P. Barbati

有一些设计模式可以满足复杂构造的需求——如果不能简洁地完成,请创建工厂方法或工厂类。

使用最新的 java 和添加的 lambda,很容易创建一个可以接受任何你想要的初始化代码的构造函数。

class LambdaInitedClass {

   public LamdaInitedClass(Consumer<LambdaInitedClass> init) {
       init.accept(this);
   }
}

用...调用它

 new LambdaInitedClass(l -> { // init l any way you want });

A
Akash Manngroliya

是的,可以使用 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);
   }

C
Community

关键字 this 可用于从构造函数调用构造函数,当为一个类编写多个构造函数时,有时您希望从另一个构造函数调用一个构造函数以避免重复代码。

Bellow 是一个链接,我解释了有关构造函数和 getters() 和 setters() 的其他主题,并且我使用了一个具有两个构造函数的类。我希望解释和示例对您有所帮助。

Setter methods or constructors


J
John McClane

您可以通过 this(...) 关键字(当您需要从同一个类调用构造函数时)或 super(...) 关键字(当您需要从超类调用构造函数时)调用另一个构造函数。

但是,这样的调用必须是构造函数的 first 语句。要克服此限制,请使用 this answer


P
Paul Chu

我知道这个问题有很多例子,但我发现我放在这里是为了分享我的想法。有两种方法可以链接构造函数。在同一类中,您可以使用此关键字。在继承中,您需要使用 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");
        }
    }

O
Omar Faroque Anik

它被称为伸缩构造函数反模式或构造函数链接。是的,你绝对可以。我在上面看到了很多例子,我想补充一点,如果你知道你只需要两个或三个构造函数,那可能没问题。但如果您需要更多,请尝试使用不同的设计模式,如 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


C
Community

是的,您可以从另一个构造函数调用构造函数。例如:

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;
    }
}

您还可以从 Constructor Chaining in Java 阅读详细信息


有什么办法可以颠倒过来吗?从动物调用无参数动物构造函数?我试过了,看起来它不起作用。
r
rogerdpack

最初来自 Mirko Klemm 的分析器,稍作修改以解决问题:

仅出于完整性考虑:还有 Instance initialization block 始终在调用任何其他构造函数之前执行。它仅由类定义主体中某处的语句块“{ ... }”组成。你甚至可以拥有不止一个。你不能调用它们,但如果你想跨构造函数重用一些代码,它们就像“共享构造函数”代码,类似于调用方法。

所以在你的情况下

{ 
  System.out.println("this is shared constructor code executed before the constructor");
  field1 = 3;
}

还有一个“静态”版本来初始化静态成员:“static { ... }”


a
ansh sachdeva

我更喜欢这种方式:

    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;
    }
}

k
keshari abeysinghe

它被称为构造函数链接。构造函数链接是相对于当前对象从另一个构造函数调用一个构造函数的过程。构造函数链接可以通过两种方式完成:

1.在同一个类中:对于同一个类的构造函数,可以使用this()关键字来完成。 2.从基类:使用super()关键字从基类调用构造函数。