자바에서 명명 된 매개 변수 관용구


81

Java에서 Named Parameter 관용구를 구현하는 방법은 무엇입니까? (특히 생성자)

JavaBeans에서 사용되는 구문이 아닌 Objective-C와 같은 구문을 찾고 있습니다.

작은 코드 예제가 좋습니다.

감사.

답변:


105

생성자에서 키워드 인수를 시뮬레이션하기 위해 내가 본 최고의 Java 관용구는 Effective Java 2nd Edition에 설명 된 Builder 패턴 입니다.

기본 아이디어는 다른 생성자 매개 변수에 대한 setter (일반적으로 getter가 아님)가있는 Builder 클래스를 갖는 것입니다. 도있다 build()방법. Builder 클래스는 빌드하는 데 사용되는 클래스의 (정적) 중첩 클래스 인 경우가 많습니다. 외부 클래스의 생성자는 종종 비공개입니다.

최종 결과는 다음과 같습니다.

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

Foo의 인스턴스를 만들려면 다음과 같이 작성합니다.

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

주요주의 사항은 다음과 같습니다.

  1. 패턴 설정은 매우 장황합니다 (보시다시피). 많은 곳에서 인스턴스화하려는 클래스를 제외하고는 그만한 가치가 없을 것입니다.
  2. 모든 매개 변수가 정확히 한 번 지정되었는지 컴파일 타임 검사가 없습니다. 런타임 검사를 추가하거나 선택적 매개 변수에만 이것을 사용하고 필수 매개 변수를 Foo 또는 Builder의 생성자에 일반 매개 변수로 만들 수 있습니다. (사람들은 일반적으로 동일한 매개 변수가 여러 번 설정되는 경우에 대해 걱정하지 않습니다.)

이 블로그 게시물 (내가 아님) 도 확인하실 수 있습니다 .


12
Objective-C가 수행하는 방식에서는 실제로 명명 된 매개 변수가 아닙니다. 유창한 인터페이스처럼 보입니다. 정말 같은 것이 아닙니다.
Asaph

30
내가 사용하고자 .withFoo보다는 .setFoo: newBuilder().withSize(1).withName(1).build()보다는newBuilder().setSize(1).setName(1).build()
notnoop

17
Asaph : 네, 압니다. Java에는 명명 된 매개 변수가 없습니다. 이것이 내가 이것이 " 키워드 인수 를 시뮬레이션 하기 위해 내가 본 최고의 Java 관용구"라고 말한 이유 입니다. Objective-C의 "명명 된 매개 변수"는 특정 순서를 강제하기 때문에 이상적이지 않습니다. Lisp 또는 Python과 같은 진정한 키워드 인수가 아닙니다. 적어도 Java Builder 패턴에서는 실제 키워드 인수처럼 순서가 아닌 이름 만 기억하면됩니다.
Laurence Gonsalves

14
notnoop : 빌더의 상태를 변경하는 setter이기 때문에 "set"을 선호합니다. 예, "with"는 모든 것을 연결하는 단순한 경우에 멋지게 보이지만 자체 변수에 Builder가있는 더 복잡한 경우 (조건부로 속성을 설정하기 때문일 수 있음) I like that the set 접두사는 이러한 메서드가 호출 될 때 Builder가 변경되고 있음을 완전히 명확하게합니다. "with"접두사는 기능적으로 들리며 이러한 메서드는 확실히 작동하지 않습니다.
Laurence Gonsalves

4
There's no compile-time checking that all of the parameters have been specified exactly once.이 문제는 인터페이스를 반환함으로써 극복 될 수 Builder1BuilderN여기서 각 커버 어느 족의 하나 이상의 build(). 코드가 훨씬 더 장황하지만 DSL에 대한 컴파일러 지원과 함께 제공되며 자동 완성 작업을 매우 편리하게 만듭니다.
rsp

73

이것은 언급 할 가치가 있습니다.

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

소위 이중 중괄호 이니셜 라이저 . 실제로 인스턴스 이니셜 라이저가있는 익명 클래스입니다.


26
흥미로운 기술이지만 코드에서 사용할 때마다 새 클래스를 생성하므로 약간 비싸 보입니다.
Red Hyena

6
자동 서식 지정, 서브 클래 싱 및 직렬화 경고를 제외하고는 실제로 속성 기반 초기화를위한 C # 구문과 매우 유사합니다. 그러나 4.0 이후의 C #에는 매개 변수가 지정되어 있으므로 프로그래머는 나중에 발에서 자신을 쏘지 못하게하는 관용구를 시뮬레이션해야하는 Java 프로그래머와 달리 선택의 여지가 없습니다.
Distortum

3
이것이 가능하다는 것을보고 다행 이었지만 Red Hyena가 지적한 것처럼이 솔루션은 비용이 많이 들기 때문에 저는 반대 투표를해야했습니다. 자바가 실제로 Python처럼 명명 된 매개 변수를 지원할 때까지 기다릴 수 없습니다.
Gattster 2013

12
찬성. 이것은 가장 읽기 쉽고 간결한 방식으로 질문에 답합니다. 좋아. "성능이 없다"입니다. 여기서 우리는 몇 밀리 초와 비트를 추가로 이야기하고 있습니까? 그들 중 하나? 수십? 날 오해하지 마세요 - 내가 자바 너트 자세한 a로 실행 싫어 나는이를 사용하지 않는 (말장난 의도)
스티브

2
완전한! 공개 / 보호 필드 만 필요합니다. 그것은 확실히 최고의 솔루션이며 빌더보다 훨씬 적은 오버 헤드를 유발합니다. Hyena / Gattster : 의견을 작성하기 전에 (1) JLS를 읽고 (2) 생성 된 바이트 코드를 확인하십시오.
Jonatan Kaźmierczak

21

여기에서 조언을 따를 수도 있습니다 : http://www.artima.com/weblogs/viewpost.jsp?thread=118828

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

호출 사이트에서는 장황하지만 전반적으로 오버 헤드가 가장 낮습니다.


3
좋은 원인은 낮은 오버 헤드이지만 너무 끔찍한 느낌입니다. 인수가 많은 경우에는 아마도 Builder () 메서드를 사용할 것입니다.
Gattster 2013

23
나는 이것이 명명 된 매개 변수의 요점을 완전히 놓친다고 생각합니다. ( 이름과 값을 연관시키는 무언가 가 있습니다 ). 아무 표시가 없다 무엇이든지 당신이 순서를 반대로하는 경우는. 이렇게하는 대신 단순히 코멘트를 추가하는 것에 대한 조언을 doIt( /*value*/ 13, /*location*/ 47, /*overwrite*/ true )
드리고 싶습니다

20

자바 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);

    }
}
  • 명명 된 매개 변수
  • 인수 순서 수정
  • 정적 검사-> 이름없는 사람이 불가능합니다.
  • 같은 유형의 인수를 우연히 전환하기가 어렵습니다 (텔레 스코프 생성자에서 가능한 것처럼)

3
좋은. 가변 인수 순서가없는 것이 얼마나 어리석은가. (하지만 이것을 사용하겠다는 말은 아닙니다 ...)
Scheintod

1
이것은 훌륭한 아이디어입니다. 그 정의는 create()내 트랙에서 나를 멈췄다. Java에서 그런 스타일의 람다 체인을 본 적이 없습니다. 람다를 사용하여 다른 언어로이 아이디어를 처음 발견 했습니까?
kevinarpe

2
커링이라고합니다 : en.wikipedia.org/wiki/Currying . Btw : 아마도 영리한 아이디어일지도 모르지만이 명명 된 인수 스타일을 권장하지 않습니다. 나는 많은 논증을 가진 실제 프로젝트에서 그것을 테스트했고 그것은 읽기 어렵고 코드를 탐색하기 어렵게 만들었다.
Alex

결국 Java에는 매개 변수라는 이름의 Visual Basic 스타일이 제공됩니다. C ++는 그렇지 않기 때문에 Java는 이전에 그렇지 않았습니다. 그러나 우리는 결국 거기에 도달 할 것입니다. 저는 자바 다형성의 90 %가 선택적 매개 변수를 해킹하고 있다고 말하고 싶습니다.
Tuntable

7

Java는 생성자 또는 메소드 인수에 대해 Objective-C와 유사한 명명 된 매개 변수를 지원하지 않습니다. 게다가 이것은 실제로 Java 방식이 아닙니다. Java에서 일반적인 패턴은 자세한 이름이 지정된 클래스 및 멤버입니다. 클래스와 변수는 명사 여야하고 명명 된 메서드는 동사 여야합니다. 나는 당신이 창의력을 발휘하고 Java 명명 규칙에서 벗어날 수 있고 해키 방식으로 Objective-C 패러다임을 에뮬레이션 할 수 있다고 생각하지만 이것은 코드 유지 관리를 담당하는 평균 Java 개발자가 특별히 높이 평가하지 않을 것입니다. 어떤 언어로 작업 할 때 특히 팀에서 작업 할 때 언어 및 커뮤니티의 관습을 고수해야합니다.


4
+1-현재 사용하고있는 언어의 관용구를 고수하는 것에 대한 조언을 얻으십시오. 당신의 코드를 읽어야 할 다른 사람들을 생각하십시오!
Stephen C

3
나는 당신이 좋은 지적을한다고 생각하기 때문에 당신의 대답을 찬성했습니다. 왜 당신이 반대표를 받았는지 짐작해야한다면 아마도 이것이 질문에 대한 답이 아니기 때문일 것입니다. Q : "Java에서 명명 된 매개 변수를 어떻게 수행합니까?" A : "You do n't"
Gattster 2013

12
나는 당신의 대답이 질문과 관련이 없다고 생각하기 때문에 반대 투표를했습니다. 자세한 이름은 매개 변수 순서 문제를 실제로 해결하지 못합니다. 예, 이름으로 인코딩 할 수는 있지만 분명히 실용적이지 않습니다. 무관 한 패러다임을 불러 온다고해서 하나의 패러다임이 지원되지 않는 이유가 설명되지 않습니다.
Andreas Mueller

7

Java 6을 사용하는 경우 변수 매개 변수를 사용하고 정적을 가져 와서 훨씬 더 나은 결과를 생성 할 수 있습니다. 이에 대한 자세한 내용은 다음에서 찾을 수 있습니다.

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

요컨대, 다음과 같은 것을 가질 수 있습니다.

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

2
나는 그것을 좋아하지만 여전히 문제의 절반 만 해결합니다. Java에서는 필요한 값에 대한 컴파일 시간 검사를 잃지 않고 실수로 매개 변수가 변경되는 것을 방지 할 수 없습니다.
cdunn2001

타입 안전성이 없다면 이것은 단순한 주석보다 더 나쁩니다.
피터 데이비스

7

이 스타일은 다른 언어에있는 getset 접두사 없이 명명 된 매개 변수속성 기능을 모두 처리한다는 점을 지적하고 싶습니다 . Java 영역에서는 일반적이지 않지만 특히 다른 언어를 처리 한 경우 이해하기 어렵지 않고 더 간단합니다.

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

이건 어떤가요

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
build ()에 대한 몇 가지 제약 조건을 확인할 수 있기 때문에 Builder가 훨씬 좋습니다. 그러나 나는 또한 설정 / 접두사가없는 짧은 인수를 선호합니다.
rkj

4
또한 빌더 패턴은 빌드 된 클래스 (이 경우 Tiger)를 변경 불가능하게 만들 수 있기 때문에 더 좋습니다.
Jeff Olson

2

인수에 이름을 지정하는 일반적인 생성자와 정적 메서드를 사용할 수 있습니다.

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

}

용법:

import static Something.*;

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

실제 명명 된 매개 변수와 비교 한 제한 사항 :

  • 인수 순서가 적절하다
  • 단일 생성자로 가변 인수 목록을 사용할 수 없습니다.
  • 모든 인수에 대한 방법이 필요합니다.
  • 댓글보다별로 좋지 않습니다 (new Something ( /*name*/ "pen", /*size*/ 20, /*weight*/ 8.2))

선택권이 있다면 Scala 2.8을 살펴보십시오. http://www.scala-lang.org/node/2075


2
이 방법의 한 가지 단점은 당신이 해야 올바른 순서로 인수를 얻을. 위 코드를 사용하면 다음과 같이 작성할 수 있습니다. Something s = new Something (name ( "pen"), size (20), size (21)); 또한이 방법은 선택적 인수를 입력하는 것을 방지하는 데 도움이되지 않습니다.
Matt Quail

1
나는 분석이 upvote에 다음과 같습니다 not really better than a comment... 다른 한편으로는 ...)
Scheintod

2

Java 8의 람다를 사용하면 실제 명명 된 매개 변수에 더 가까워 질 수 있습니다 .

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

이것은 아마도 $기호를 사용하는 모든 것과 같은 몇 가지 "자바 모범 사례"를 위반할 것입니다 .

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

장점 :

  • 지금까지 본 어떤 빌더 패턴보다 상당히 짧음
  • 메서드와 생성자 모두에서 작동합니다.
  • 완전한 유형 안전
  • 다른 프로그래밍 언어의 실제 명명 된 매개 변수와 매우 유사합니다.
  • 일반적인 빌더 패턴만큼 안전합니다 (매개 변수를 여러 번 설정할 수 있음).

단점 :

  • 당신의 상사가 아마 당신을 린치 할 것입니다.
  • 무슨 일이 일어나고 있는지 말하기가 더 어렵습니다.

1
단점 : 필드는 공개되어 있으며 최종적이지 않습니다. 이것에 동의한다면 왜 setter를 사용하지 않습니까? 방법에 대해 어떻게 작동합니까?
Alex

세터를 사용할 수 있지만 그 점은 무엇입니까? 그것은 단지 코드를 더 길게 만들고 이렇게하는 것의 이점을 제거 할 것입니다. 할당은 부작용이없고 setter는 블랙 박스입니다. $foo호출자에게 탈출하지 (누군가가 콜백의 변수 내부에 할당하지 않는 한) 왜 수 없습니다 그들은 공개?
Vic

2

프로젝트 Lombok의 @Builder 주석 을 사용하여 Java에서 명명 된 매개 변수를 시뮬레이션 할 수 있습니다 . 이렇게하면 모든 클래스의 새 인스턴스를 만드는 데 사용할 수있는 빌더가 생성됩니다 (작성한 클래스와 외부 라이브러리에서 가져온 클래스 모두).

다음은 클래스에서 활성화하는 방법입니다.

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

나중에 다음과 같이 사용할 수 있습니다.

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

라이브러리에서 오는 클래스에 대한 빌더를 생성하려면 다음과 같이 주석이 달린 정적 메서드를 생성합니다.

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

이렇게하면 다음에서 호출 할 수있는 "builder"라는 메서드가 생성됩니다.

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

Google 자동 / 값은 유사한 목적을 제공하지만 주석 처리 프레임 워크를 사용합니다. 이는 프로젝트 Lombocks 바이트 코드 조작보다 훨씬 더 안전합니다 (JVM 업그레이드 후에도 여전히 작동 함).
René

1
Google 자동 / 값은 Lombok을 사용하는 것에 비해 약간의 추가 마일이 필요하다고 생각합니다. Lombok의 접근 방식은 전통적인 JavaBean 작성과 어느 정도 호환됩니다 (예 : 새를 통해 인스턴스화 할 수 있고, 디버거에서 필드가 제대로 표시되는 등). 나는 또한 Lombok이 사용하는 바이트 코드 조작 + IDE 플러그인 솔루션의 열렬한 팬은 아니지만 실제로는 정상적으로 작동한다는 것을 인정해야합니다. 지금까지 JDK 버전 변경, 반영 등에 문제가 없습니다.
Istvan Devai

응 그것은 사실이야. 자동 / 값의 경우 구현 될 추상 클래스를 제공해야합니다. Lombok에는 훨씬 적은 코드가 필요합니다. 따라서 장단점을 비교해야합니다.
René

2

나는 "주석 해결 방법"이 자체 대답을 할 가치가 있다고 생각합니다 (기존 답변에 숨겨져 있고 여기에 주석에 언급 됨).

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

1

이것은 Builder위에서 Lawrence가 설명한 패턴 의 변형입니다 .

나는 이것을 (적절한 장소에서) 많이 사용하고 있음을 발견했습니다.

가장 큰 차이점은이 경우 Builder는 불완전하다는 것 입니다. 이것은 재사용가능 하고 스레드로부터 안전하다는 장점이 있습니다.

따라서 이것을 사용하여 하나의 기본 빌더 를 만든 다음 필요한 여러 위치에서 구성하고 개체를 빌드 할 수 있습니다.

이는 동일한 객체를 반복해서 빌드하는 경우 가장 합리적입니다. 빌더를 정적으로 만들 수 있고 설정 변경에 대해 걱정할 필요가 없기 때문입니다.

반면에 매개 변수를 변경하여 개체를 만들어야하는 경우 약간의 오버 헤드가 발생합니다. (그러나 정적 / 동적 생성을 사용자 정의 build방법 과 결합 할 수 있습니다 )

다음은 예제 코드입니다.

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

Java의 모든 솔루션은 매우 장황 할 수 있지만 Google AutoValuesImmutables 와 같은 도구 가 JDK 컴파일 시간 주석 처리를 사용하여 자동으로 빌더 클래스를 생성 한다는 점을 언급 할 가치가 있습니다.

필자의 경우 이름이 지정된 매개 변수가 Java 열거 형에서 사용되기를 원했기 때문에 열거 형 인스턴스를 다른 클래스에서 인스턴스화 할 수 없기 때문에 빌더 패턴이 작동하지 않습니다. @deamon의 답변과 유사한 접근 방식을 생각해 냈지만 컴파일 시간에 매개 변수 순서 확인을 추가합니다 (더 많은 코드 비용으로)

다음은 클라이언트 코드입니다.

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

그리고 구현 :

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

karg 라이브러리에서 지원하는 관용구 는 고려할 가치가 있습니다.

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

이것은 기본적으로 R Casha답변 과 동일 하지만 설명하는 코드가없는 것 같습니다 .
Scheintod 2015 년

0

다음은 컴파일러 확인 빌더 패턴입니다. 주의 사항 :

  • 이것은 인수의 이중 할당을 막을 수 없습니다
  • 당신은 좋은 .build()방법을 가질 수 없습니다
  • 필드 당 하나의 일반 매개 변수

따라서 통과하지 못하면 실패 할 클래스 외부의 무언가가 필요합니다 Builder<Yes, Yes, Yes>. getSum예제로 정적 메서드를 참조하십시오 .

class No {}
class Yes {}

class Builder<K1, K2, K3> {
  int arg1, arg2, arg3;

  Builder() {}

  static Builder<No, No, No> make() {
    return new Builder<No, No, No>();
  }

  @SuppressWarnings("unchecked")
  Builder<Yes, K2, K3> arg1(int val) {
    arg1 = val;
    return (Builder<Yes, K2, K3>) this;
  }

  @SuppressWarnings("unchecked")
  Builder<K1, Yes, K3> arg2(int val) {
    arg2 = val;
    return (Builder<K1, Yes, K3>) this;
  }

  @SuppressWarnings("unchecked")
  Builder<K1, K2, Yes> arg3(int val) {
    this.arg3 = val;
    return (Builder<K1, K2, Yes>) this;
  }

  static int getSum(Builder<Yes, Yes, Yes> build) {
    return build.arg1 + build.arg2 + build.arg3;
  }

  public static void main(String[] args) {
    // Compiles!
    int v1 = getSum(make().arg1(44).arg3(22).arg2(11));
    // Builder.java:40: error: incompatible types:
    // Builder<Yes,No,Yes> cannot be converted to Builder<Yes,Yes,Yes>
    int v2 = getSum(make().arg1(44).arg3(22));
    System.out.println("Got: " + v1 + " and " + v2);
  }
}

주의 사항 설명 . 왜 빌드 방법이 없습니까? 문제는 그것이 Builder클래스에있을 것이고 K1, K2, K3, 등 으로 매개 변수화 될 것이라는 것입니다 . 메소드 자체가 컴파일되어야하므로 호출하는 모든 것이 컴파일되어야합니다. 따라서 일반적으로 클래스 자체의 메서드에 컴파일 테스트를 넣을 수 없습니다.

비슷한 이유로 빌더 모델을 사용하여 이중 할당을 방지 할 수 없습니다.


-1

@irreputable이 좋은 해결책을 내놓았습니다. 그러나-유효성 검사 및 일관성 검사가 발생하지 않으므로 클래스 인스턴스가 잘못된 상태로 남을 수 있습니다. 따라서 빌더 클래스를 하위 클래스로 만들더라도 추가 하위 클래스가 생성되는 것을 피하면서이를 Builder 솔루션과 결합하는 것을 선호합니다. 또한 추가 빌더 클래스로 인해 더 장황 해지기 때문에 람다를 사용하여 메서드를 하나 더 추가했습니다. 완전성을 위해 다른 빌더 접근 방식 중 일부를 추가했습니다.

다음과 같이 클래스로 시작합니다.

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

}

그런 다음 이것을 사용하여 다른 방법을 적용합니다.

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

@LaurenceGonsalves가 이미 게시 한 내용에서 부분적으로 완전히 뜯어 낸 것처럼 보이지만 선택한 규칙의 작은 차이를 볼 수 있습니다.

JLS가 명명 된 매개 변수를 구현한다면 어떻게할까요? 짧은 형식의 지원을 제공하여 기존 관용구 중 하나를 확장 할 수 있습니까? 또한 Scala는 명명 된 매개 변수를 어떻게 지원합니까?

흠-조사하기에 충분하며 새로운 질문 일 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.