Thành ngữ tham số được đặt tên trong Java


81

Làm thế nào để triển khai thành ngữ Tham số được đặt tên trong Java? (đặc biệt đối với các nhà xây dựng)

Tôi đang tìm một cú pháp giống Objective-C và không giống cú pháp được sử dụng trong JavaBeans.

Một ví dụ mã nhỏ sẽ ổn.

Cảm ơn.

Câu trả lời:


103

Thành ngữ Java tốt nhất mà tôi có vẻ để mô phỏng các đối số từ khóa trong các hàm tạo là mẫu Builder, được mô tả trong Phiên bản Java thứ 2 hiệu quả .

Ý tưởng cơ bản là có một lớp Builder có các bộ định tuyến (nhưng thường không phải bộ chuyển đổi) cho các tham số phương thức khởi tạo khác nhau. Ngoài ra còn có một build()phương pháp. Lớp Builder thường là một lớp lồng nhau (tĩnh) của lớp mà nó được sử dụng để xây dựng. Phương thức khởi tạo của lớp bên ngoài thường là private.

Kết quả cuối cùng trông giống như sau:

public class Foo {
  public static class Builder {
    public Foo build() {
      return new Foo(this);
    }

    public Builder setSize(int size) {
      this.size = size;
      return this;
    }

    public Builder setColor(Color color) {
      this.color = color;
      return this;
    }

    public Builder setName(String name) {
      this.name = name;
      return this;
    }

    // you can set defaults for these here
    private int size;
    private Color color;
    private String name;
  }

  public static Builder builder() {
      return new Builder();
  }

  private Foo(Builder builder) {
    size = builder.size;
    color = builder.color;
    name = builder.name;
  }

  private final int size;
  private final Color color;
  private final String name;

  // The rest of Foo goes here...
}

Để tạo một phiên bản Foo, sau đó bạn viết một cái gì đó như:

Foo foo = Foo.builder()
    .setColor(red)
    .setName("Fred")
    .setSize(42)
    .build();

Những lưu ý chính là:

  1. Việc thiết lập mô hình khá dài dòng (như bạn có thể thấy). Có lẽ không có giá trị ngoại trừ các lớp học mà bạn có kế hoạch thực hiện ở nhiều nơi.
  2. Không có kiểm tra thời gian biên dịch để đảm bảo rằng tất cả các tham số đã được chỉ định chính xác một lần. Bạn có thể thêm kiểm tra thời gian chạy hoặc bạn có thể chỉ sử dụng điều này cho các tham số tùy chọn và đặt các tham số bắt buộc là tham số bình thường cho Foo hoặc phương thức khởi tạo của Builder. (Mọi người thường không lo lắng về trường hợp cùng một tham số được đặt nhiều lần.)

Bạn cũng có thể muốn xem bài đăng trên blog này (không phải của tôi).


12
Đó thực sự không phải là các tham số được đặt tên theo cách Objective-C thực hiện chúng. Điều đó trông giống như một giao diện thông thạo hơn. Nó thực sự không giống nhau.
Asaph

29
Tôi thích sử dụng .withFoo, chứ không phải là .setFoo: newBuilder().withSize(1).withName(1).build()hơnnewBuilder().setSize(1).setName(1).build()
notnoop

16
Asaph: vâng, tôi biết. Java không có các tham số được đặt tên. Đó là lý do tại sao tôi nói đây là "thành ngữ Java tốt nhất mà tôi có vẻ để mô phỏng các đối số từ khóa". "Các tham số được đặt tên" của Objective-C cũng ít hơn lý tưởng, vì chúng bắt buộc một thứ tự cụ thể. Chúng không phải là đối số từ khóa đúng như trong Lisp hoặc Python. Ít nhất với Java Builder pattern, bạn chỉ cần nhớ tên, không phải thứ tự, giống như các đối số từ khóa thực.
Laurence Gonsalves

14
notnoop: Tôi thích "set" hơn vì đây là những setters làm thay đổi trạng thái của Builder. Có, "with" trông đẹp trong trường hợp đơn giản khi bạn đang xâu chuỗi mọi thứ lại với nhau, nhưng trong những trường hợp phức tạp hơn khi bạn có Bộ dựng trong biến riêng của nó (có lẽ vì bạn đang đặt thuộc tính có điều kiện) Tôi thích bộ tiền tố làm cho nó hoàn toàn rõ ràng rằng Builder đang bị đột biến khi các phương thức này được gọi. Tiền tố "with" nghe có vẻ có chức năng đối với tôi, và các phương pháp này chắc chắn không hoạt động.
Laurence Gonsalves

4
There's no compile-time checking that all of the parameters have been specified exactly once.Vấn đề này có thể được khắc phục bằng cách trả lại giao diện Builder1để BuilderNnơi mỗi bìa hoặc một trong những setters hoặc build(). Nó dài dòng hơn nhiều để viết mã, nhưng nó đi kèm với hỗ trợ trình biên dịch cho DSL của bạn và làm cho tự động hoàn thành rất tốt để làm việc.
rsp

72

Đây là điều đáng nói:

Foo foo = new Foo() {{
    color = red;
    name = "Fred";
    size = 42;
}};

cái gọi là bộ khởi tạo dấu ngoặc kép . Nó thực sự là một lớp ẩn danh với bộ khởi tạo cá thể.


26
Kỹ thuật thú vị nhưng có vẻ hơi tốn kém vì nó sẽ tạo ra một lớp mới mỗi khi tôi sử dụng nó trong mã của mình.
Red Hyena vào

6
Bỏ qua các cảnh báo định dạng tự động, phân lớp con và tuần tự hóa, điều này thực sự khá gần với cú pháp C # để khởi tạo dựa trên thuộc tính. Tuy nhiên, C # thời 4.0 cũng có các tham số được đặt tên, vì vậy các lập trình viên thực sự tha hồ lựa chọn, không giống như các lập trình viên Java phải mô phỏng các thành ngữ để tránh việc họ tự bắn vào chân sau này.
Distortum 27/12/12

3
Rất vui khi thấy điều này có thể thực hiện được nhưng tôi đã phải từ chối vì giải pháp này đắt tiền như Red Hyena đã chỉ ra. Không thể đợi cho đến khi Java thực sự hỗ trợ các tham số được đặt tên như Python.
Gattster

12
Ủng hộ. Điều này trả lời câu hỏi theo cách dễ đọc, ngắn gọn nhất. Khỏe. Nó "không biểu diễn". Chúng ta đang nói ở đây bao nhiêu mili giây & bit thừa? Ai trong số họ? Hàng chục? Đừng hiểu sai ý tôi - tôi sẽ không sử dụng cái này vì tôi không muốn bị thực thi bởi một hạt Java dài dòng (ý định chơi chữ;)
steve

2
Hoàn hảo! Chỉ yêu cầu các trường công khai / được bảo vệ. Đó chắc chắn là giải pháp tốt nhất, ít gây ra chi phí hơn nhiều so với trình xây dựng. Hyena / Gattster: vui lòng (1) đọc JLS và (2) kiểm tra mã bytecode được tạo trước khi viết nhận xét của bạn.
Jonatan Kaźmierczak

21

Bạn cũng có thể thử làm theo lời khuyên từ đây: http://www.artima.com/weblogs/viewpost.jsp?thread=118828

int value; int location; boolean overwrite;
doIt(value=13, location=47, overwrite=true);

Nó dài dòng trên trang web cuộc gọi, nhưng nhìn chung cho chi phí thấp nhất.


3
Tốt vì chi phí thấp, nhưng nó cảm thấy rất khó hiểu. Tôi có thể sẽ sử dụng phương thức Builder () cho các trường hợp có nhiều đối số.
Gattster

23
Tôi nghĩ rằng điều này hoàn toàn bỏ sót điểm của các tham số được đặt tên. (có một cái gì đó liên kết tên với giá trị ). Không có bất kỳ dấu hiệu nào nếu bạn đảo ngược thứ tự. Thay vì làm tôi điều này sẽ tư vấn việc chỉ thêm một nhận xét:doIt( /*value*/ 13, /*location*/ 47, /*overwrite*/ true )
Scheintod

20

Kiểu Java 8:

public class Person {
    String name;
    int age;

    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    static PersonWaitingForName create() {
        return name -> age -> new Person(name, age);
    }

    static interface PersonWaitingForName {
        PersonWaitingForAge name(String name);
    }

    static interface PersonWaitingForAge {
        Person age(int age);
    }

    public static void main(String[] args) {

        Person charlotte = Person.create()
            .name("Charlotte")
            .age(25);

    }
}
  • tham số được đặt tên
  • sửa thứ tự của các đối số
  • kiểm tra tĩnh -> không thể có người không tên
  • khó chuyển đổi các đối số cùng loại một cách tình cờ (giống như có thể xảy ra trong các hàm tạo telescop)

3
đẹp. Thật là đáng tiếc khi nó không có thứ tự đối số thay đổi. (Nhưng không phải để nói rằng tôi sẽ sử dụng này ...)
Scheintod

1
Đây là một ý tưởng tuyệt vời. Định nghĩa về create()đã ngăn tôi theo dõi. Tôi chưa bao giờ thấy kiểu chuỗi lambda đó trong Java. Lần đầu tiên bạn phát hiện ra ý tưởng này bằng một ngôn ngữ khác với lambdas?
kevinarpe

2
Nó được gọi là cà ri: en.wikipedia.org/wiki/Currying . Btw: Có lẽ đó là một ý tưởng thông minh, nhưng tôi sẽ không đề xuất phong cách lập luận được đặt tên này. Tôi đã thử nghiệm nó trong một dự án thực tế với nhiều đối số và nó dẫn đến mã khó đọc và khó điều hướng.
Alex,

Cuối cùng, Java sẽ được cung cấp các tham số được đặt tên theo kiểu Visual Basic. Java thì không trước đây vì C ++ thì không. Nhưng cuối cùng chúng ta sẽ đến đó. Tôi muốn nói rằng 90% tính đa hình của Java chỉ là hack xung quanh các tham số tùy chọn.
Tuntable

7

Java không hỗ trợ các tham số có tên giống Objective-C cho các hàm tạo hoặc đối số phương thức. Hơn nữa, đây thực sự không phải là cách hoạt động của Java. Trong java, mẫu điển hình là các lớp và thành viên được đặt tên chi tiết. Các lớp và biến phải là danh từ và phương thức được đặt tên là động từ. Tôi cho rằng bạn có thể sáng tạo và đi ngược lại các quy ước đặt tên của Java và mô phỏng theo mô hình Objective-C theo cách khó hiểu nhưng điều này sẽ không được các nhà phát triển Java trung bình chịu trách nhiệm duy trì mã của bạn đánh giá cao. Khi làm việc bằng bất kỳ ngôn ngữ nào, bạn cần tuân thủ các quy ước của ngôn ngữ và cộng đồng, đặc biệt là khi làm việc theo nhóm.


4
+1 - cho lời khuyên về việc bám sát các thành ngữ của ngôn ngữ bạn hiện đang sử dụng. Hãy nghĩ đến những người khác sẽ cần đọc mã của bạn!
Stephen C

3
Tôi ủng hộ câu trả lời của bạn bởi vì tôi nghĩ rằng bạn làm cho một điểm tốt. Nếu tôi phải đoán tại sao bạn nhận được phiếu phản đối, thì có lẽ vì điều này không trả lời được câu hỏi. H: "Làm cách nào để thực hiện các tham số được đặt tên trong Java?" A: "Bạn không"
Gattster

12
Tôi đã phản đối vì tôi nghĩ câu trả lời của bạn không liên quan gì đến câu hỏi. Tên dài dòng không thực sự giải quyết được vấn đề về thứ tự tham số. Có, bạn có thể mã hóa chúng bằng tên nhưng điều đó rõ ràng là không khả thi. Đưa ra một mô hình không liên quan không giải thích tại sao một mô hình không được hỗ trợ.
Andreas Mueller

7

Nếu bạn đang sử dụng Java 6, bạn có thể sử dụng các tham số biến và nhập tĩnh để tạo ra kết quả tốt hơn nhiều. Chi tiết về điều này được tìm thấy trong:

http://zinzel.blogspot.com/2010/07/creating-methods-with-name-parameters.html

Trong ngắn hạn, bạn có thể có một cái gì đó như:

go();
go(min(0));
go(min(0), max(100));
go(max(100), min(0));
go(prompt("Enter a value"), min(0), max(100));

2
Tôi thích nó, nhưng nó vẫn chỉ giải quyết được một nửa vấn đề. Trong Java, bạn không thể ngăn các tham số vô tình bị hoán đổi mà không mất kiểm tra thời gian biên dịch cho các giá trị bắt buộc.
cdunn2001

Nếu không có sự an toàn kiểu thì điều này còn tệ hơn các bình luận // đơn giản.
Peter Davis

7

Tôi muốn chỉ ra rằng kiểu này giải quyết cả tham số được đặt tên và các tính năng thuộc tính mà không tiền tố getset mà ngôn ngữ khác có. Nó không thông thường trong lĩnh vực Java nhưng đơn giản hơn, không khó hiểu, đặc biệt nếu bạn đã xử lý các ngôn ngữ khác.

public class Person {
   String name;
   int age;

   // name property
   // getter
   public String name() { return name; }

   // setter
   public Person name(String val)  { 
    name = val;
    return this;
   }

   // age property
   // getter
   public int age() { return age; }

   // setter
   public Person age(int val) {
     age = val;
     return this;
   }

   public static void main(String[] args) {

      // Addresses named parameter

      Person jacobi = new Person().name("Jacobi").age(3);

      // Addresses property style

      println(jacobi.name());
      println(jacobi.age());

      //...

      jacobi.name("Lemuel Jacobi");
      jacobi.age(4);

      println(jacobi.name());
      println(jacobi.age());
   }
}

6

Thế còn

public class Tiger {
String myColor;
int    myLegs;

public Tiger color(String s)
{
    myColor = s;
    return this;
}

public Tiger legs(int i)
{
    myLegs = i;
    return this;
}
}

Tiger t = new Tiger().legs(4).color("striped");

5
Builder tốt hơn nhiều, vì bạn có thể kiểm tra một số ràng buộc trên build (). Nhưng tôi cũng thích các đối số ngắn hơn mà không đặt / có tiền tố.
rkj

4
Ngoài ra, mẫu xây dựng tốt hơn vì nó cho phép bạn làm cho lớp đã xây dựng (trong trường hợp này là Tiger) là bất biến.
Jeff Olson

2

Bạn có thể sử dụng một hàm tạo thông thường và các phương thức tĩnh để đặt tên cho các đối số:

public class Something {

    String name;
    int size; 
    float weight;

    public Something(String name, int size, float weight) {
        this.name = name;
        this.size = size;
        this.weight = weight;
    }

    public static String name(String name) { 
        return name; 
    }

    public static int size(int size) {
        return size;
    }

    public float weight(float weight) {
        return weight;
    }

}

Sử dụng:

import static Something.*;

Something s = new Something(name("pen"), size(20), weight(8.2));

Hạn chế so với các tham số được đặt tên thực:

  • thứ tự đối số có liên quan
  • danh sách đối số biến không thể thực hiện được với một hàm tạo
  • bạn cần một phương pháp cho mọi đối số
  • không thực sự tốt hơn một nhận xét (Cái gì đó mới ( /*name*/ "pen", /*size*/ 20, /*weight*/ 8.2))

Nếu bạn có sự lựa chọn, hãy xem Scala 2.8. http://www.scala-lang.org/node/2075


2
Một nhược điểm của phương pháp này là bạn phải lấy các đối số theo đúng thứ tự. Đoạn mã trên sẽ cho phép bạn viết: Something s = new Something (name ("pen"), size (20), size (21)); Ngoài ra, cách tiếp cận này không giúp bạn tránh nhập các đối số tùy chọn.
Matt Cút vào

1
Tôi sẽ upvote này để phân tích: not really better than a comment... mặt khác ...;)
Scheintod

2

Sử dụng lambdas của Java 8, bạn có thể tiến gần hơn đến các tham số được đặt tên thực .

foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});

Xin lưu ý rằng điều này có thể vi phạm một vài "phương pháp hay nhất của java" (như bất kỳ thứ gì sử dụng $biểu tượng).

public class Main {
  public static void main(String[] args) {
    // Usage
    foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});
    // Compare to roughly "equivalent" python call
    // foo(foo = -10, bar = "hello", array = [1, 2, 3, 4])
  }

  // Your parameter holder
  public static class $foo {
    private $foo() {}

    public int foo = 2;
    public String bar = "test";
    public int[] array = new int[]{};
  }

  // Some boilerplate logic
  public static void foo(Consumer<$foo> c) {
    $foo foo = new $foo();
    c.accept(foo);
    foo_impl(foo);
  }

  // Method with named parameters
  private static void foo_impl($foo par) {
    // Do something with your parameters
    System.out.println("foo: " + par.foo + ", bar: " + par.bar + ", array: " + Arrays.toString(par.array));
  }
}

Ưu điểm:

  • Ngắn hơn đáng kể so với bất kỳ mẫu xây dựng nào mà tôi đã thấy cho đến nay
  • Hoạt động cho cả phương thức và hàm tạo
  • Loại hoàn toàn an toàn
  • Nó trông rất gần với các tham số được đặt tên thực tế trong các ngôn ngữ lập trình khác
  • Nó an toàn như mẫu trình tạo thông thường của bạn (có thể đặt thông số nhiều lần)

Nhược điểm:

  • Sếp của bạn có thể sẽ yêu cầu bạn vì điều này
  • Thật khó để biết chuyện gì đang xảy ra

1
Nhược điểm: các trường là công khai và không phải là cuối cùng. Nếu bạn ổn với điều này, tại sao không chỉ sử dụng setters? Nó hoạt động như thế nào đối với các phương pháp?
Alex

Có thể sử dụng setters nhưng vấn đề ở đây là gì? Nó chỉ làm cho mã dài hơn và điều đó sẽ loại bỏ lợi ích của việc làm như thế này. Việc chỉ định không có tác dụng phụ và những người sắp xếp là hộp đen. $fookhông bao giờ thoát cho người gọi (trừ khi ai đó gán nó cho một biến bên trong lệnh gọi lại), vậy tại sao chúng không thể công khai?
Vic

2

Bạn có thể sử dụng chú thích @Builder của dự án Lombok để mô phỏng các tham số được đặt tên trong Java. Điều này sẽ tạo ra một trình tạo cho bạn mà bạn có thể sử dụng để tạo các phiên bản mới của bất kỳ lớp nào (cả các lớp bạn đã viết và những lớp đến từ các thư viện bên ngoài).

Đây là cách kích hoạt nó trên một lớp:

@Getter
@Builder
public class User {
    private final Long id;
    private final String name;
}

Sau đó, bạn có thể sử dụng nó bằng cách:

User userInstance = User.builder()
    .id(1L)
    .name("joe")
    .build();

Nếu bạn muốn tạo một Bộ dựng như vậy cho một lớp đến từ thư viện, hãy tạo một phương thức tĩnh có chú thích như sau:

class UserBuilder {
    @Builder(builderMethodName = "builder")
    public static LibraryUser newLibraryUser(Long id, String name) {
        return new LibraryUser(id, name);
    }
  }

Điều này sẽ tạo ra một phương thức có tên "người xây dựng" có thể được gọi bằng:

LibraryUser user = UserBuilder.builder()
    .id(1L)
    .name("joe")
    .build();

Google auto / value phục vụ một mục đích tương tự nhưng sử dụng khung xử lý chú thích, an toàn hơn nhiều (công cụ sẽ vẫn hoạt động sau khi nâng cấp JVM) so với thao tác mã byte Lombocks của dự án.
René

1
Tôi nghĩ rằng giá trị / tự động của Google đòi hỏi thêm một chút so với việc sử dụng Lombok. Cách tiếp cận của Lombok ít nhiều tương thích với cách viết JavaBean truyền thống (ví dụ: bạn có thể khởi tạo thông qua mới, các trường hiển thị đúng cách trong trình gỡ lỗi, v.v.). Tôi cũng không phải là một fan hâm mộ lớn của giải pháp plugin mã byte + IDE mà Lombok sử dụng, nhưng tôi phải thừa nhận rằng trong thực tế, nó hoạt động tốt. Không có vấn đề cho đến nay với những thay đổi JDK phiên bản, phản ánh, vv
Istvan Devai

Đúng là như vậy. Đối với auto / value, bạn cần cung cấp một lớp trừu tượng sau đó sẽ được thực thi. Lombok cần ít mã hơn rất nhiều. Vì vậy, ưu và nhược điểm cần được so sánh.
René

2

Tôi cảm thấy như "comment-workaround" xứng đáng là câu trả lời của riêng nó (ẩn trong các câu trả lời hiện có và được đề cập trong phần nhận xét ở đây).

someMethod(/* width */ 1024, /* height */ 768);

1

Đây là một biến thể của BuilderMẫu như đã được Lawrence mô tả ở trên.

Tôi thấy mình sử dụng cái này rất nhiều (ở những nơi thích hợp).

Sự khác biệt chính là, trong trường hợp này, Builder có thể miễn dịch . Điều này có ưu điểm là nó có thể được tái sử dụng và an toàn cho các chủ đề.

Vì vậy, bạn có thể sử dụng nó để tạo một Builder mặc định và sau đó ở những nơi khác nhau mà bạn cần, bạn có thể cấu hình nó và xây dựng đối tượng của mình.

Điều này hoàn toàn hợp lý, nếu bạn đang xây dựng lặp đi lặp lại cùng một đối tượng, vì khi đó bạn có thể làm cho trình tạo tĩnh và không phải lo lắng về việc thay đổi cài đặt của nó.

Mặt khác, nếu bạn phải xây dựng các đối tượng với paramaters thay đổi, điều này có một số chi phí yên tĩnh. (nhưng này, bạn có thể kết hợp tạo tĩnh / động với các buildphương thức tùy chỉnh )

Đây là mã ví dụ:

public class Car {

    public enum Color { white, red, green, blue, black };

    private final String brand;
    private final String name;
    private final Color color;
    private final int speed;

    private Car( CarBuilder builder ){
        this.brand = builder.brand;
        this.color = builder.color;
        this.speed = builder.speed;
        this.name = builder.name;
    }

    public static CarBuilder with() {
        return DEFAULT;
    }

    private static final CarBuilder DEFAULT = new CarBuilder(
            null, null, Color.white, 130
    );

    public static class CarBuilder {

        final String brand;
        final String name;
        final Color color;
        final int speed;

        private CarBuilder( String brand, String name, Color color, int speed ) {
            this.brand = brand;
            this.name = name;
            this.color = color;
            this.speed = speed;
        }
        public CarBuilder brand( String newBrand ) {
            return new CarBuilder( newBrand, name, color, speed );
        }
        public CarBuilder name( String newName ) {
            return new CarBuilder( brand, newName, color, speed );
        }
        public CarBuilder color( Color newColor ) {
            return new CarBuilder( brand, name, newColor, speed );
        }
        public CarBuilder speed( int newSpeed ) {
            return new CarBuilder( brand, name, color, newSpeed );
        }
        public Car build() {
            return new Car( this );
        }
    }

    public static void main( String [] args ) {

        Car porsche = Car.with()
                .brand( "Porsche" )
                .name( "Carrera" )
                .color( Color.red )
                .speed( 270 )
                .build()
                ;

        // -- or with one default builder

        CarBuilder ASSEMBLY_LINE = Car.with()
                .brand( "Jeep" )
                .name( "Cherokee" )
                .color( Color.green )
                .speed( 180 )
                ;

        for( ;; ) ASSEMBLY_LINE.build();

        // -- or with custom default builder:

        CarBuilder MERCEDES = Car.with()
                .brand( "Mercedes" )
                .color( Color.black )
                ;

        Car c230 = MERCEDES.name( "C230" ).speed( 180 ).build(),
            clk = MERCEDES.name( "CLK" ).speed( 240 ).build();

    }
}

1

Bất kỳ giải pháp nào trong Java có thể sẽ khá dài dòng, nhưng điều đáng nói là các công cụ như Google AutoValuesImmutable sẽ tự động tạo các lớp trình tạo cho bạn bằng cách sử dụng xử lý chú thích thời gian biên dịch JDK.

Đối với trường hợp của tôi, tôi muốn các tham số được đặt tên để sử dụng trong một enum Java, vì vậy một mẫu trình tạo sẽ không hoạt động vì các trường hợp enum không thể được khởi tạo bởi các lớp khác. Tôi đã nghĩ ra một cách tiếp cận tương tự câu trả lời của @ deamon nhưng thêm kiểm tra thời gian biên dịch của thứ tự tham số (với chi phí nhiều mã hơn)

Đây là mã khách hàng:

Person p = new Person( age(16), weight(100), heightInches(65) );

Và việc thực hiện:

class Person {
  static class TypedContainer<T> {
    T val;
    TypedContainer(T val) { this.val = val; }
  }
  static Age age(int age) { return new Age(age); }
  static class Age extends TypedContainer<Integer> {
    Age(Integer age) { super(age); }
  }
  static Weight weight(int weight) { return new Weight(weight); }
  static class Weight extends TypedContainer<Integer> {
    Weight(Integer weight) { super(weight); }
  }
  static Height heightInches(int height) { return new Height(height); }
  static class Height extends TypedContainer<Integer> {
    Height(Integer height) { super(height); }
  }

  private final int age;
  private final int weight;
  private final int height;

  Person(Age age, Weight weight, Height height) {
    this.age = age.val;
    this.weight = weight.val;
    this.height = height.val;
  }
  public int getAge() { return age; }
  public int getWeight() { return weight; }
  public int getHeight() { return height; }
}

0

Thành ngữ được hỗ trợ bởi thư viện karg có thể đáng xem xét:

class Example {

    private static final Keyword<String> GREETING = Keyword.newKeyword();
    private static final Keyword<String> NAME = Keyword.newKeyword();

    public void greet(KeywordArgument...argArray) {
        KeywordArguments args = KeywordArguments.of(argArray);
        String greeting = GREETING.from(args, "Hello");
        String name = NAME.from(args, "World");
        System.out.println(String.format("%s, %s!", greeting, name));
    }

    public void sayHello() {
        greet();
    }

    public void sayGoodbye() {
        greet(GREETING.of("Goodbye");
    }

    public void campItUp() {
        greet(NAME.of("Sailor");
    }
}

Điều này dường như về cơ bản giống như R Cashaanswer nhưng không có mã để giải thích nó.
Scheintod

-1

@irreputable đã đưa ra một giải pháp hay. Tuy nhiên - nó có thể khiến cá thể Lớp của bạn ở trạng thái không hợp lệ, vì sẽ không có việc xác thực và kiểm tra tính nhất quán. Do đó, tôi muốn kết hợp điều này với giải pháp Builder, tránh việc tạo thêm lớp con, mặc dù nó vẫn sẽ là lớp con của lớp người xây dựng. Ngoài ra, vì lớp trình tạo thêm làm cho nó dài dòng hơn, tôi đã thêm một phương thức nữa bằng cách sử dụng lambda. Tôi đã thêm một số phương pháp tiếp cận trình xây dựng khác để hoàn thiện.

Bắt đầu với một lớp như sau:

public class Foo {
  static public class Builder {
    public int size;
    public Color color;
    public String name;
    public Builder() { size = 0; color = Color.RED; name = null; }
    private Builder self() { return this; }

    public Builder size(int size) {this.size = size; return self();}
    public Builder color(Color color) {this.color = color; return self();}
    public Builder name(String name) {this.name = name; return self();}

    public Foo build() {return new Foo(this);}
  }

  private final int size;
  private final Color color;
  private final String name;

  public Foo(Builder b) {
    this.size = b.size;
    this.color = b.color;
    this.name = b.name;
  }

  public Foo(java.util.function.Consumer<Builder> bc) {
    Builder b = new Builder();
    bc.accept(b);
    this.size = b.size;
    this.color = b.color;
    this.name = b.name;
  }

  static public Builder with() {
    return new Builder();
  }

  public int getSize() { return this.size; }
  public Color getColor() { return this.color; }  
  public String getName() { return this.name; }  

}

Sau đó, sử dụng điều này áp dụng các phương pháp khác nhau:

Foo m1 = new Foo(
  new Foo.Builder ()
  .size(1)
  .color(BLUE)
  .name("Fred")
);

Foo m2 = new Foo.Builder()
  .size(1)
  .color(BLUE)
  .name("Fred")
  .build();

Foo m3 = Foo.with()
  .size(1)
  .color(BLUE)
  .name("Fred")
  .build();

Foo m4 = new Foo(
  new Foo.Builder() {{
    size = 1;
    color = BLUE;
    name = "Fred";
  }}
);

Foo m5 = new Foo(
  (b)->{
    b.size = 1;
    b.color = BLUE;
    b.name = "Fred";
  }
);

Có vẻ như một phần nào đó là sự tách rời hoàn toàn từ những gì @LaurenceGonsalves đã đăng, nhưng bạn sẽ thấy sự khác biệt nhỏ trong quy ước được chọn.

Tôi tự hỏi, nếu JLS sẽ triển khai các tham số được đặt tên, thì họ sẽ làm như thế nào? Liệu họ có đang mở rộng một trong những thành ngữ hiện có bằng cách cung cấp hỗ trợ dạng ngắn cho nó không? Ngoài ra Scala hỗ trợ các tham số được đặt tên như thế nào?

Hmmm - đủ để nghiên cứu, và có thể là một câu hỏi mới.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.