Câu hỏi Làm thế nào để gọi một hàm khởi tạo từ một hàm khác trong Java?


Có thể gọi một hàm tạo từ một hàm khác (trong cùng một lớp, không phải từ một lớp con) không? Nếu có thì sao? Và những gì có thể là cách tốt nhất để gọi một nhà xây dựng khác (nếu có một số cách để làm điều đó)?


1828
2017-11-12 20:10


gốc


kiểm tra điều này quá: yegor256.com/2015/05/28/one-primary-constructor.html - yegor256
Tôi tin rằng tiền đề của câu hỏi của bạn là sai. Thay vì gọi một hàm tạo trong một hàm tạo, hãy sử dụng mẫu Nhà máy. Một phương thức nhà máy tĩnh đầu tiên tạo ra tất cả các đối tượng cấp thấp hơn. Sau đó, nó xây dựng các đối tượng cấp cao hơn được trả về từ cuộc gọi nhà máy. Kỹ thuật này loại bỏ sự phức tạp khỏi mô hình hỗ trợ bảo trì, rõ ràng và thử nghiệm. - David Medinets


Các câu trả lời:


Có, có thể:

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

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

Để liên kết với một hàm tạo siêu lớp cụ thể thay vì một trong cùng một lớp, hãy sử dụng super thay vì this. Lưu ý rằng bạn chỉ có thể xích vào một nhà xây dựngnó phải là câu lệnh đầu tiên trong phần thân hàm tạo của bạn.

Xem thêm câu hỏi liên quan này, đó là về C # nhưng cũng áp dụng cùng một nguyên tắc.


2468
2017-11-12 20:12



Vì vậy, tôi cho rằng nó không thể gọi một nhà xây dựng siêu và một nhà xây dựng của cùng một lớp như cả hai cần phải là dòng đầu tiên? - gsingh2011
@ gsingh2011: Thật vậy. Bạn chỉ có thể chuỗi một người xây dựng khác. - Jon Skeet
Điều này phải xuất hiện trên dòng đầu tiên, nhưng bạn có thể thực hiện các phép tính trong hàm dựng trước khi nó được gọi: Bạn có thể sử dụng các phương thức tĩnh trong các đối số của this () trên dòng đầu tiên và đóng gói bất kỳ phép tính nào phải được thực hiện trước cuộc gọi đối với hàm tạo khác trong phương thức tĩnh đó. (Tôi đã thêm câu trả lời này làm câu trả lời riêng). - Christian Fries
@ gsingh2011 Tôi biết nó trễ nhưng như một cách xung quanh, bạn có thể gọi constructor quá tải bằng cách sử dụng này (...) và sau đó trong constructor quá tải đó, bạn có thể thực hiện cuộc gọi đến constructor lớp cơ sở bằng cách sử dụng super (...) - Ali
@JustinTime: Một lần nữa, nó phụ thuộc vào ý bạn bằng cách "tạo" - đối tượng được "tạo ra" trong đó bộ nhớ của nó được cấp phát và kiểu được đặt trước khi bất kỳ phần tử tạo nào được thi hành. Constructors đang khởi tạo hơn là tạo. Đặc biệt, loại đối tượng là loại "cuối cùng" ngay từ đầu - vì vậy nếu bạn gọi bất kỳ phương thức ảo nào từ các nhà xây dựng, bạn sẽ nhận được ghi đè cụ thể nhất được gọi là. Tôi tin rằng điều này khác với C ++. - Jon Skeet


Sử dụng this(args). Mẫu được ưu tiên là làm việc từ hàm tạo nhỏ nhất đến lớn nhất.

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

Bạn cũng có thể sử dụng phương pháp tiếp cận được ủng hộ gần đây hơn của valueOf hoặc chỉ "của":

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

Để gọi một lớp học siêu hạng, hãy sử dụng super(asdf). Các cuộc gọi đến siêu phải là cuộc gọi đầu tiên trong constructor hoặc bạn sẽ nhận được một lỗi trình biên dịch.


198
2018-03-11 20:33



Nếu sử dụng nhiều tham số hàm tạo, hãy xem xét trình tạo. Xem khoản 2 của "Java hiệu quả" của Joshua Bloch. - koppor
Vấn đề với việc thực hiện phương pháp tiếp cận cuối cùng bằng cách sử dụng phương pháp nhà máy, newCons, là bạn đang cố gắng thay đổi trạng thái của một đối tượng, sử dụng setArg1(...), điều đó có khả năng nhất sẽ có trường được đặt là trường cuối cùng. Vì chúng tôi đang cố gắng giữ càng nhiều càng tốt của một đối tượng bất biến, nếu không hoàn toàn, một mẫu trình xây dựng sẽ giải quyết vấn đề này chính xác hơn. - YoYo
Bạn sẽ không thích làm :: public Cons () {this (madeUpArg1Value, madeUpArg2Value); } - LordHieros
Việc khởi tạo nên tiến hành từ ít nhất đến lớn nhất - tôi sẽ không bao giờ có một hàm tạo mặc định gọi lên chuỗi cho một hàm tạo đa tham số. Những gì cần phải xảy ra là tất cả các nhà xây dựng gọi hoặc là mặc định hoặc một nhà xây dựng với các tham số ít hơn. - Rodney P. Barbati
@ RodneyP.Barbati Nó khá phổ biến trong Java cho các nhà xây dựng có độ tinh khiết thấp hơn để gọi các nhà thầu lớn hơn. và sau đó không làm gì khác. nếu một lớp K có, ví dụ: hai trường cuối cùng a, b, thì "hàm tạo chung" sẽ là K(A a, B b) { this.a = a; this.b = b; }. Sau đó nếu b có một giá trị mặc định hợp lý, có thể có một hàm tạo một-arg K(A a) { this(a, DEFAULT_B); }và nếu có mặc định a là tốt, chúng tôi có một hàm tạo mặc định: K() { this(DEFAULT_A); }. Đó là một quy ước khá phổ biến trong Java. - Joshua Taylor


[Lưu ý: Tôi chỉ muốn thêm một khía cạnh mà tôi không thấy trong các câu trả lời khác: cách vượt qua các giới hạn của yêu cầu rằng () này phải nằm trên dòng đầu tiên).]

Trong Java, một hàm tạo khác của cùng một lớp có thể được gọi từ một hàm tạo thông qua this(). Tuy nhiên, hãy lưu ý rằng this phải nằm trên hàng đầu tiên.

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

Cái đó this phải xuất hiện trên dòng đầu tiên trông giống như một giới hạn lớn, nhưng bạn có thể xây dựng các đối số của các hàm tạo khác thông qua các phương thức tĩnh. Ví dụ:

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



Đúng là bạn có thể gọi các phương thức tĩnh theo cách này để thực hiện các phép tính phức tạp cho các giá trị đối số, điều đó là tốt. Tuy nhiên, nếu một người cảm thấy mã đó là cần thiết trước khi tiến hành ủy nhiệm (this(...)) sau đó sẽ là hợp lý để giả định rằng một sai lầm khủng khiếp đã được thực hiện ở đâu đó và rằng thiết kế có lẽ cần một chút suy nghĩ lại. - Engineer Dollery
Tôi đồng ý rằng rất chuyển đổi phức tạp có thể cho thấy một vấn đề thiết kế. Nhưng 1) có một số biến đổi đơn giản mà điều này có thể hữu ích - không phải tất cả các nhà xây dựng chỉ là phép chiếu tuyến tính trên người khác và 2) có thể có tình huống khác nơi thông tin này có thể trở thành tay, như hỗ trợ mã cũ. (Trong khi tôi đồng ý về kết luận của bạn, tôi không thấy lý do tại sao nó sẽ biện minh cho một cuộc bỏ phiếu xuống). - Christian Fries
Điều này hoàn toàn trái ngược với những gì tôi sẽ đề nghị - constructor không có tham số nên khởi tạo tất cả các giá trị mặc định. Hàm tạo tham số 2 nên gọi hàm tạo không có tham số, sau đó khởi tạo 2 giá trị mà nó nhận được. Hàm tạo tham số 3 nên gọi hàm tạo tham số 2, sau đó khởi tạo giá trị thứ 3 cho giá trị mà nó nhận được. Làm như được hiển thị có nghĩa là bạn phải làm nhiều việc hơn để thêm một tham số khác. - Rodney P. Barbati
@ RodneyP.Barbati: Tôi thấy một vài vấn đề khi thực hiện nó theo cách bạn mô tả nó: a) Làm theo cách đó không thể minh họa việc sử dụng phương thức tĩnh trong một hàm tạo (và đó là ý định của ví dụ); -) và b) nếu bạn làm theo cách của bạn, các trường không thể final (các trường cuối cùng chỉ có thể được khởi tạo một lần). - Christian Fries
@ RodneyP.Barbati: Hai khía cạnh khác: c) Tôi tin rằng bạn nên luôn luôn làm khởi tạo đối tượng tại một điểm duy nhất, mà phải là nhà xây dựng chung nhất. Nếu khởi tạo đối tượng yêu cầu một nhiệm vụ phức tạp (đối tượng init không được lười biếng) hoặc kiểm tra hoặc mua một số tài nguyên (như một tệp), thì bạn chỉ muốn làm điều đó một lần. Và d) Thêm một đối số khác (nói đối số4) mà initialisation phụ thuộc vào giá trị của đối số1 cho đối số3, bạn sẽ phải thay đổi tất cả các hàm tạo trong trường hợp của bạn, trong khi ở đây bạn chỉ phải thêm một tham số và gọi 3 arg -arg constructor. - Christian Fries


Khi tôi cần gọi một hàm tạo khác từ bên trong mã (không phải trên dòng đầu tiên), tôi thường sử dụng một phương thức trợ giúp như sau:

class MyClass {
   int field;


   MyClass() {
      init(0);
   } 
   MyClass(int value) {
      if (value<0) {
          init(0);
      } 
      else { 
          init(value);
      }
   }
   void init(int x) {
      field = x;
   }
}

Nhưng thông thường tôi cố gắng làm điều đó theo cách khác bằng cách gọi các nhà xây dựng phức tạp hơn từ những người đơn giản hơn trên dòng đầu tiên, đến mức có thể. Đối với ví dụ trên

class MyClass {
   int field;

   MyClass(int value) {
      if (value<0)
         field = 0;
      else
         field = value;
   }
   MyClass() {
      this(0);
   }
}

36
2018-05-26 15:09





Trong một hàm tạo, bạn có thể sử dụng this từ khóa để gọi một hàm tạo khác trong cùng một lớp. Làm như vậy được gọi là lời gọi hàm tạo rõ ràng.

Đây là một lớp hình chữ nhật khác, với một thực hiện khác nhau từ một trong các đối tượng phần.

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

}

Lớp này chứa một tập hợp các hàm tạo. Mỗi hàm tạo khởi tạo một số hoặc tất cả các biến thành viên của hình chữ nhật.


23
2018-05-07 22:52



tại sao bạn không gọi hàm khởi tạo thứ hai Rectangle(int width, int height) trong Rectangle() thay vì Rectangle(int x, int y, int width, int height) ? - ANjaNA
Một constructor mặc định không nên có kiến ​​thức về các hàm tạo mức cao hơn - nó là mặc định. Theo mô hình này sẽ dẫn đến việc bạn phải thay đổi một hoặc nhiều nhà xây dựng hiện có khi bạn thêm một công cụ mới. Ví dụ, thêm một giá trị lineWidth và xem những gì tôi có ý nghĩa. Nhưng có khởi tạo mặc định tất cả các giá trị, và đảo ngược chuỗi hàm tạo, bạn sẽ thấy mỗi constructor xây dựng trên trước và chỉ khởi tạo các giá trị mà nó hỗ trợ cụ thể - Bạn có thể thêm một giá trị mới mà không thay đổi giá trị hiện tại. Có nhiều mô hình phổ biến trong java không phải là mẫu tốt. - Rodney P. Barbati


Như mọi người đã nói, bạn sử dụng this(…), được gọi là lời gọi hàm tạo rõ ràng.

Tuy nhiên, hãy nhớ rằng trong tuyên bố lời gọi hàm tạo rõ ràng như vậy bạn không thể tham khảo

  • bất kì các biến mẫu hoặc là
  • bất kì các phương thức mẫu hoặc là
  • bất kì lớp bên trong được khai báo trong lớp này hoặc bất kỳ lớp cha nào, hoặc
  • this hoặc là
  • super.

Như đã nêu trong JLS (§8.8.7.1).


12
2017-11-21 13:14





Tôi sẽ nói với bạn một cách dễ dàng

hai loại nhà thầu:

  1. Nhà xây dựng mặc định
  2. Nhà xây dựng tham số

Tôi sẽ giải thích trong một ví dụ

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

Trong ví dụ trên tôi đã cho thấy 3 loại gọi

  1. this () gọi này phải là câu lệnh đầu tiên trong hàm tạo
  2. Đây là Tên đối tượng ít hơn. điều này sẽ tự động gọi hàm khởi tạo mặc định. 3.Điều này gọi cho hàm tạo tham số.

Chú thích: đây phải là câu lệnh đầu tiên trong hàm tạo.


7
2017-11-27 19:01



Bạn có những điều sau đây trong phương pháp chính: //điều này(); lỗi vì "phải là câu lệnh đầu tiên trong hàm tạo  Tuyên bố này không có ý nghĩa nhiều. Nếu bạn đang cố gắng nói rằng điều này() không thể gọi từ bên trong chủ yếu phương pháp, sau đó có nó không thể được bởi vì chính là tĩnh và sẽ không có tham chiếu đến điều này() - S R Chaitanya
đó là những gì tôi đang truyền đạt ... câu hỏi mới trong nhận xét của bạn là gì @SRChaitanya? - Shivanandam Sirmarigari
rằng bạn đang truyền đạt nó kém - Kevin Van Dyck


Bạn có thể tạo một hàm tạo từ một hàm tạo khác của cùng một lớp bằng cách sử dụng từ khóa "này". Thí dụ -

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

Đầu ra - chuỗi như hàm tạo arg .. Nhà xây dựng mặc định.. int như hàm tạo arg ..


5
2018-03-03 09:27