【问题标题】:How do I call one constructor from another in Java?如何在 Java 中从另一个构造函数调用一个构造函数?
【发布时间】:2010-09-22 01:26:51
【问题描述】:

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

【问题讨论】:

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

标签: java constructor


【解决方案1】:

是的,有可能:

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(...) 调用重载构造函数,然后在该重载构造函数中,您可以使用调用基类的构造函数超级(...)
  • @Andrej:这确实是“从另一个构造函数调用一个构造函数”,但它不会做 OP 想要的,即通过多个构造函数初始化单个对象,一个链接到另一个。通过在一个构造函数调用中创建一个单独的对象来创建两个对象根本不是一回事。
【解决方案2】:

使用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(...) 更改对象的状态,该对象很可能具有其字段设置为最终。当我们试图尽可能地保持对象不可变时,如果不是完全不可变的话,构建器模式将更正确地解决这个问题。
  • 你不想这样做 :: 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 字段(因此必须设置它),那么默认构造函数必须设置它。如果您的更高数量的构造函数调用默认构造函数(这必须在其他任何事情之前完成),那么更高数量的构造函数永远没有任何选项来设置任何这些字段。
【解决方案3】:

[注意:我只想添加一个方面,我在其他答案中没有看到:如何克服 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() 调用。
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

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

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

    • 任何实例变量
    • 任何实例方法
    • 在此类或任何超类中声明的任何内部类,或
    • this
    • super.

    如 JLS (§8.8.7.1) 中所述。

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        【解决方案8】:

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

        【讨论】:

          【解决方案9】:

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

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

          【讨论】:

            【解决方案10】:

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

            两种类型的构造函数:

            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. 这是无名对象。这会自动调用默认构造函数。 3.这会调用Parameterized构造函数。

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

            【讨论】:

            • 你在main方法中有以下内容://this();错误,因为“必须是构造函数中的第一个语句 这个语句没有多大意义。如果你想说 this() 不能从 main内部调用> 方法,那么是的,它不可能是因为 main 是静态的并且不会引用 this()
            【解决方案11】:

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

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

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

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

            【讨论】:

              【解决方案12】:

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

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

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

              【讨论】:

                【解决方案13】:

                关键字this可以用来从一个构造函数中调用一个构造函数,当为一个类编写多个构造函数时,有时你想调用一个避免重复代码。

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

                Setter methods or constructors

                【讨论】:

                  【解决方案14】:

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

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

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

                  用...调用它

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

                  【讨论】:

                    【解决方案15】:

                    很简单

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

                    希望这会有所帮助。

                    【讨论】:

                      【解决方案16】:

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

                      【讨论】:

                        【解决方案17】:

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

                        【讨论】:

                          【解决方案18】:

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

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

                          【讨论】:

                            【解决方案19】:

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

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

                            所以你的情况

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

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

                            【讨论】:

                              【解决方案20】:

                              我更喜欢这种方式:

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

                              【讨论】:

                                【解决方案21】:

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

                                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

                                【讨论】:

                                • 有什么办法可以颠倒过来吗?从动物调用无参数动物构造函数?我试过了,好像不行。
                                【解决方案22】:

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

                                例子:-

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

                                【讨论】:

                                  【解决方案23】:

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

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

                                  【讨论】:

                                    猜你喜欢
                                    • 2016-07-03
                                    • 2011-03-24
                                    • 2015-07-02
                                    • 1970-01-01
                                    • 2014-03-12
                                    • 1970-01-01
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多