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


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


1828
2017-11-12 20:10


起源


检查一下: yegor256.com/2015/05/28/one-primary-constructor.html - yegor256
我相信你的问题的前提是错误的。不使用构造函数中的构造函数,而是使用Factory模式。静态工厂方法首先创建所有较低级别的对象。然后它构造从工厂调用返回的更高级别的对象。该技术消除了模型的复杂性,有助于维护,清晰度和测试。 - David Medinets


答案:


对的,这是可能的:

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

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

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

也可以看看 这个相关的问题,这是关于C#,但适用相同的原则。


2468
2017-11-12 20:12



所以我认为不可能调用超级构造函数和同一类的另一个构造函数,因为它们都需要成为第一行? - gsingh2011
@ gsingh2011:的确如此。你只能链接到 一 其他构造函数。 - Jon Skeet
这必须出现在第一行,但您可以在调用之前在构造函数中进行计算:您可以在第一行的this()的参数中使用静态方法,并封装在调用之前必须执行的任何计算到那个静态方法中的另一个构造函数。 (我已将此作为单独的答案添加)。 - Christian Fries
@ gsingh2011我知道它已经晚了但是作为一种方法,你可以使用this(...)调用重载的构造函数,然后在那个重载的构造函数中,你可以使用super(...)调用基类的构造函数 - Ali
@JustinTime:同样,它取决于“创建”的含义 - 对象是“创建的”,因为它的内存已分配,并且在执行任何构造函数体之前设置类型。构造函数是初始化而不是创建。特别是,对象的类型从一开始就是它的“最终”类型 - 所以如果你从构造函数调用任何虚方法,你将获得最具体的覆盖调用。我相信这与C ++不同。 - Jon Skeet


运用 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(asdf)。对super的调用必须是构造函数中的第一个调用,否则您将收到编译器错误。


198
2018-03-11 20:33



如果使用了许多构造函数参数,请考虑构建器。参见Joshua Bloch的“Effective Java”第2项。 - koppor
使用工厂方法实现最后一种方法的问题, newCons,是你正试图改变一个对象的状态,使用 setArg1(...),最有可能将其字段设置为final。因为我们试图尽可能地保持对象不可变,如果不是完全的,构建器模式将更正确地解决这个问题。 - YoYo
你不宁愿做:: public Cons(){this(madeUpArg1Value,madeUpArg2Value); } - LordHieros
初始化应该从最小到最大 - 我永远不会有默认构造函数调用链到多参数构造函数。需要做的是所有构造函数都调用默认值或具有较少参数的构造函数。 - Rodney P. Barbati
@ RodneyP.Barbati在Java中,低级的构造函数调用更大的构造函数是很常见的 然后什么都不做。如果类K具有例如两个最终字段a,b,那么“一般构造函数”将是 K(A a, B b) { this.a = a; this.b = b; }。然后,如果 b 有一个合理的默认值,可以有一个arg构造函数 K(A a) { this(a, DEFAULT_B); },如果有默认值 a 同样,我们有一个默认的构造函数: K() { this(DEFAULT_A); }。这是Java中非常常见的惯例。 - Joshua Taylor


[注意:我只想添加一个方面,我在其他答案中没有看到:如何克服这个()必须在第一行的要求的限制。]

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

  }

}

177
2018-04-23 23:12



确实,您可以通过这种方式调用静态方法,以便对参数值执行复杂的计算,这很好。但是,如果在构造函数委派之前感觉需要代码(this(...)那么可以合理地假设在某个地方犯了一个可怕的错误,并且设计可能需要重新思考一下。 - Engineer Dollery
我同意一个 非常 复杂的转型可能意味着设计问题。但是1)有一些简单的转换,这可能是有用的 - 并非所有构造函数都只是对其他构造函数的线性投影,2)可能存在其他情况,这些信息可能成为手,如支持遗留代码。 (虽然我同意你的结论,但我不明白为什么它会证明投票是正当的)。 - Christian Fries
这与我的建议完全相反 - 无参数构造函数应将所有值初始化为默认值。 2参数构造函数应该调用no param构造函数,然后初始化它接收的2个值。 3参数构造函数应该调用2参数构造函数,然后将第3个值初始化为它接收的值。如图所示,这意味着您需要做更多工作才能添加其他参数。 - Rodney P. Barbati
@ RodneyP.Barbati:我按照你描述它的方式看到了一些问题:a)这样做是不可能在构造函数中说明静态方法的使用(这是示例的意图); - )和b)如果按照自己的方式进行,那么这些字段就不可能 final (最终字段只能初始化一次)。 - Christian Fries
@ RodneyP.Barbati:另外两个方面:c)我相信你应该总是在一个点进行对象初始化,这必须是最通用的构造函数。如果对象初始化需要复杂的任务(对象init不是懒惰)或检查或获取某些资源(如文件),那么您只想这样做一次。并且d)添加另一个参数(比如argument4),其初始化取决于argument1到argument3的值,你必须在你的情况下改变所有构造函数,而在这里你只需要添加一个并让3-arg调用4 -arg构造函数。 - Christian Fries


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

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

36
2018-05-26 15:09





在构造函数中,您可以使用 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;
    }

}

该类包含一组构造函数。每个构造函数初始化一些或所有矩形的成员变量。


23
2018-05-07 22:52



为什么不调用第二个构造函数呢? Rectangle(int width, int height) 在 Rectangle() 代替 Rectangle(int x, int y, int width, int height) ? - ANjaNA
默认构造函数不应该具有更高级别构造函数的知识 - 它是默认构造函数。遵循此模式将导致您在添加新构造函数时必须更改一个或多个现有构造函数。例如,添加一个lineWidth值,看看我的意思。但是默认初始化所有值,并反转构造函数链,您将看到每个构造函数构建在先前,并初始化它专门支持的值 - 您可以添加新的值而不更改现有的值。 java中有许多常见的模式并不是好的模式。 - Rodney P. Barbati


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

但是,请记住这一点 在这样一个显式的构造函数调用语句中 你可能不会参考

  • 任何 实例变量 要么
  • 任何 实例方法 要么
  • 任何 内部阶级 在此类或任何超类中声明,或
  • this 要么
  • super

如JLS(§8.8.7.1)中所述。


12
2017-11-21 13:14





我会告诉你一个简单的方法

 构造函数的类型:

  1. 默认构造函数
  2. 参数化构造函数

我将在一个例子中解释

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种类型的呼叫

  1. this()调用this必须是构造函数中的第一个语句
  2. 这是Name less Object。这会自动调用默认构造函数。 3.这会调用参数化构造函数。

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


7
2017-11-27 19:01



主方法中包含以下内容: //这个();错误,因为“必须是构造函数中的第一个语句  这句话没有多大意义。如果你想说的话 这个() 不能从里面打电话 主要 方法,然后是的,它不能是因为main是静态的,不会引用 这个() - S R Chaitanya
这就是我要传达的内容..你评论中的新内容@SRChaitanya? - Shivanandam Sirmarigari
你传达的很差 - Kevin Van Dyck


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


5
2018-03-03 09:27