Java Builder 클래스 서브 클래 싱


133

이 박사 돕 스는 기사 우리는 빌더를 서브 클래스의 경우를 처리 어떻게, 특히 빌더 패턴을? GMO 라벨링을 추가하기 위해 서브 클래스로 만들려는 예제의 컷 다운 버전을 취하면 다음과 같은 순진한 구현이 있습니다.

public class NutritionFacts {                                                                                                    

    private final int calories;                                                                                                  

    public static class Builder {                                                                                                
        private int calories = 0;                                                                                                

        public Builder() {}                                                                                                      

        public Builder calories(int val) { calories = val; return this; }                                                                                                                        

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

    protected NutritionFacts(Builder builder) {                                                                                  
        calories = builder.calories;                                                                                             
    }                                                                                                                            
}

아강:

public class GMOFacts extends NutritionFacts {                                                                                   

    private final boolean hasGMO;                                                                                                

    public static class Builder extends NutritionFacts.Builder {                                                                 

        private boolean hasGMO = false;                                                                                          

        public Builder() {}                                                                                                      

        public Builder GMO(boolean val) { hasGMO = val; return this; }                                                           

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

    protected GMOFacts(Builder builder) {                                                                                        
        super(builder);                                                                                                          
        hasGMO = builder.hasGMO;                                                                                                 
    }                                                                                                                            
}

이제 다음과 같은 코드를 작성할 수 있습니다.

GMOFacts.Builder b = new GMOFacts.Builder();
b.GMO(true).calories(100);

그러나 주문이 잘못되면 모두 실패합니다.

GMOFacts.Builder b = new GMOFacts.Builder();
b.calories(100).GMO(true);

물론 문제는 NutritionFacts.Buildera NutritionFacts.Builder가 아닌 을 반환 GMOFacts.Builder하므로이 문제를 어떻게 해결합니까? 사용하기에 더 나은 패턴이 있습니까?

참고 : 비슷한 질문에 대한이 답변 은 위의 수업을 제공합니다. 내 질문은 빌더 호출이 올바른 순서인지 확인하는 문제에 관한 것입니다.


1
다음 링크는 좋은 접근 방식을 설명합니다. egalluzzo.blogspot.co.at/2010/06/…
stuXnet

1
그러나 당신 build()은 어떻게 출력 b.GMO(true).calories(100)합니까?
Sridhar Sarnobat

답변:


170

제네릭을 사용하여 해결할 수 있습니다. 나는 이것이 "호 기적으로 반복되는 일반적인 패턴" 이라고 생각합니다

기본 클래스 빌더 메소드의 리턴 유형을 일반 인수로 만드십시오.

public class NutritionFacts {

    private final int calories;

    public static class Builder<T extends Builder<T>> {

        private int calories = 0;

        public Builder() {}

        public T calories(int val) {
            calories = val;
            return (T) this;
        }

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

    protected NutritionFacts(Builder<?> builder) {
        calories = builder.calories;
    }
}

이제 파생 클래스 빌더를 일반 인수로 사용하여 기본 빌더를 인스턴스화하십시오.

public class GMOFacts extends NutritionFacts {

    private final boolean hasGMO;

    public static class Builder extends NutritionFacts.Builder<Builder> {

        private boolean hasGMO = false;

        public Builder() {}

        public Builder GMO(boolean val) {
            hasGMO = val;
            return this;
        }

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

    protected GMOFacts(Builder builder) {
        super(builder);
        hasGMO = builder.hasGMO;
    }
}

2
흠, 나는 (a) 새로운 질문을 게시하거나, (b) implements대신에 디자인을 바꾸 extends거나, (c) 모든 것을 버려야 할 것이라고 생각 합니다. 지금은 이상한 컴파일 오류가있는 경우 leafBuilder.leaf().leaf()leafBuilder.mid().leaf()OK,하지만 leafBuilder.leaf().mid().leaf()... 실패
켄 YN에게

11
@gkamal return (T) this;unchecked or unsafe operations경고를 발생시킵니다. 피하는 것이 불가능합니다.
Dmitry Minkovsky

5
unchecked cast경고 를 해결하려면 다음 답변 중에서 제안 된 솔루션을 참조하십시오. stackoverflow.com/a/34741836/3114959
Stepan Vavra

8
Builder<T extends Builder>사실이다 rawtype -이 있어야합니다 Builder<T extends Builder<T>>.
Boris the Spider

2
@ user2957378 Builder에 대한 GMOFacts또한 제네릭해야 Builder<B extends Builder<B>> extends NutritionFacts.Builder<Builder>- 필요에 따라이 패턴은 많은 수준으로 내려 계속할 수 있습니다. 제네릭이 아닌 빌더를 선언하면 패턴을 확장 할 수 없습니다.
스파이더 보리스

44

기록을 위해, 제거하기 위해

unchecked or unsafe operations 경고

에 대한 return (T) this;@dimadima 및 @Thomas N. 이야기로 문에 대한 솔루션을 다음은 어떤 경우에 적용됩니다.

확인 abstract(일반 유형을 선언 빌더 T extends Builder이 경우)을 선포 protected abstract T getThis()다음과 같이 추상적 인 방법 :

public abstract static class Builder<T extends Builder<T>> {

    private int calories = 0;

    public Builder() {}

    /** The solution for the unchecked cast warning. */
    public abstract T getThis();

    public T calories(int val) {
        calories = val;

        // no cast needed
        return getThis();
    }

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

자세한 내용은 http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 를 참조 하십시오.


build()메소드가 NutrutionFacts를 리턴합니까?
mvd

@mvd 이것은 질문에 대한 답변이기 때문에? 하위 유형에서는 다음과 같이 재정의됩니다.public GMOFacts build() { return new GMOFacts(this); }
Stepan Vavra

문제는 우리가 둘째 아이를 추가 할 때 발생 BuilderC extends BuilderBBuilderB extends BuilderABuilderB아닌abstract
sosite

1
기본 클래스가 추상적이지 않을 수 있기 때문에 이것은 질문에 대한 답변이 아닙니다!
Roland

"일반 유형을 선언하는 빌더를 추상화하십시오"-해당 빌더를 직접 사용하려면 어떻게해야합니까?
데이지

21

블로그 게시물을 기반으로 하는 이 방법은 모든 비 리프 클래스가 추상이어야하며 모든 리프 클래스는 최종이어야합니다.

public abstract class TopLevel {
    protected int foo;
    protected TopLevel() {
    }
    protected static abstract class Builder
        <T extends TopLevel, B extends Builder<T, B>> {
        protected T object;
        protected B thisObject;
        protected abstract T createObject();
        protected abstract B thisObject();
        public Builder() {
            object = createObject();
            thisObject = thisObject();
        }
        public B foo(int foo) {
            object.foo = foo;
            return thisObject;
        }
        public T build() {
            return object;
        }
    }
}

그런 다음이 클래스와 해당 빌더를 확장하는 중간 클래스가 있으며 필요한만큼 더 있습니다.

public abstract class SecondLevel extends TopLevel {
    protected int bar;
    protected static abstract class Builder
        <T extends SecondLevel, B extends Builder<T, B>> extends TopLevel.Builder<T, B> {
        public B bar(int bar) {
            object.bar = bar;
            return thisObject;
        }
    }
}

그리고 마지막으로 부모의 모든 빌더 메소드를 순서에 관계없이 호출 할 수있는 구체적인 리프 클래스입니다.

public final class LeafClass extends SecondLevel {
    private int baz;
    public static final class Builder extends SecondLevel.Builder<LeafClass,Builder> {
        protected LeafClass createObject() {
            return new LeafClass();
        }
        protected Builder thisObject() {
            return this;
        }
        public Builder baz(int baz) {
            object.baz = baz;
            return thisObject;
        }
    }
}

그런 다음 계층 구조의 모든 클래스에서 순서에 상관없이 메소드를 호출 할 수 있습니다.

public class Demo {
    LeafClass leaf = new LeafClass.Builder().baz(2).foo(1).bar(3).build();
}

리프 클래스가 최종적인 이유를 알고 있습니까? 구체적인 클래스를 서브 클래스 화 할 수 있기를 원하지만 컴파일러가 유형을 이해하게하는 방법을 찾지 못했지만 B항상 기본 클래스로 밝혀졌습니다.
David Ganster

LeafClass의 Builder 클래스가 <T extends SomeClass, B extends SomeClass.Builder<T,B>> extends SomeClassParent.Builder<T,B>중간 SecondLevel 클래스 와 동일한 패턴을 따르지 않고 대신 특정 유형을 선언합니다. 특정 유형을 사용하여 리프에 도달 할 때까지 클래스를 인스턴스화 할 수는 없지만 일단 특정 유형을 사용하고 있고 Curiously Recurring Template Pattern을 포기했기 때문에 더 이상 확장 할 수 없습니다. 이 링크가 도움이 될 수 있습니다 : angelikalanger.com/GenericsFAQ/FAQSections/…
Q23

7

또한 calories()메소드를 대체 하고 확장 빌더를 리턴하도록 할 수 있습니다 . Java가 공변량 리턴 유형을 지원하기 때문에 컴파일됩니다 .

public class GMOFacts extends NutritionFacts {
    private final boolean hasGMO;
    public static class Builder extends NutritionFacts.Builder {
        private boolean hasGMO = false;
        public Builder() {
        }
        public Builder GMO(boolean val)
        { hasGMO = val; return this; }
        public Builder calories(int val)
        { super.calories(val); return this; }
        public GMOFacts build() {
            return new GMOFacts(this);
        }
    }
    [...]
}

아, 나는 C ++ 배경에서 왔기 때문에 그것을 몰랐다. 이 작은 예제에는 유용한 접근법이지만, 모든 클래스를 반복하는 완전한 클래스를 사용하면 모든 메소드가 고통스럽고 오류가 발생하기 쉽습니다. 그러나 새로운 것을 가르쳐 주신 +1!
Ken YN

이것은 아무것도 해결되지 않는 것 같습니다. 부모를 서브 클래 싱하는 이유 (IMO)는 부모 메서드를 재정의하지 않고 재사용하는 것입니다. 클래스가 단순 값을 설정하는 것을 제외하고 빌더 메소드에서 실제 로직이없는 값 오브젝트 인 경우 대체 메소드에서 상위 메소드를 호출하면 값이 거의 없습니다.
개발자 Dude

대답은 질문에 설명 된 문제를 해결합니다. 빌더를 사용하는 코드는 두 가지 순서로 컴파일됩니다. 한 가지 방법은 컴파일되고 다른 방법은 컴파일되지 않기 때문에 결국 어떤 가치가 있어야한다고 생각합니다.
Flavio

3

Builder패턴 에 따라 클래스를 작성하는 또 다른 방법이 있는데 , "상속보다 컴포지션 선호"를 준수합니다.

부모 클래스 Builder가 상속 할 인터페이스를 정의하십시오 .

public interface FactsBuilder<T> {

    public T calories(int val);
}

구현 NutritionFacts은 거의 동일합니다 ( Builder'FactsBuilder'인터페이스 구현 제외 ).

public class NutritionFacts {

    private final int calories;

    public static class Builder implements FactsBuilder<Builder> {
        private int calories = 0;

        public Builder() {
        }

        @Override
        public Builder calories(int val) {
            return this;
        }

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

    protected NutritionFacts(Builder builder) {
        calories = builder.calories;
    }
}

Builder아이 클래스는 (다른 일반적인 구현 제외) 동일한 인터페이스를 확장해야합니다 :

public static class Builder implements FactsBuilder<Builder> {
    NutritionFacts.Builder baseBuilder;

    private boolean hasGMO = false;

    public Builder() {
        baseBuilder = new NutritionFacts.Builder();
    }

    public Builder GMO(boolean val) {
        hasGMO = val;
        return this;
    }

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

    @Override
    public Builder calories(int val) {
        baseBuilder.calories(val);
        return this;
    }
}

이는 NutritionFacts.Builder내부 필드 GMOFacts.Builder( baseBuilder)입니다. 동일한 이름의 FactsBuilder인터페이스 호출 메소드에서 구현 된 baseBuilder메소드 :

@Override
public Builder calories(int val) {
    baseBuilder.calories(val);
    return this;
}

의 생성자에도 큰 변화가 있습니다 GMOFacts(Builder builder). 생성자의 부모 클래스 생성자에 대한 첫 번째 호출은 적절한 전달해야합니다 NutritionFacts.Builder.

protected GMOFacts(Builder builder) {
    super(builder.baseBuilder);
    hasGMO = builder.hasGMO;
}

GMOFacts클래스 의 전체 구현 :

public class GMOFacts extends NutritionFacts {

    private final boolean hasGMO;

    public static class Builder implements FactsBuilder<Builder> {
        NutritionFacts.Builder baseBuilder;

        private boolean hasGMO = false;

        public Builder() {
        }

        public Builder GMO(boolean val) {
            hasGMO = val;
            return this;
        }

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

        @Override
        public Builder calories(int val) {
            baseBuilder.calories(val);
            return this;
        }
    }

    protected GMOFacts(Builder builder) {
        super(builder.baseBuilder);
        hasGMO = builder.hasGMO;
    }
}

3

다중 빌더 상속의 전체 3 레벨 예제는 다음과 같습니다 .

(빌더 용 복사 생성자가있는 버전의 경우 아래 두 번째 예를 참조하십시오.)

첫 번째 수준-부모 (잠재적으로 요약)

import lombok.ToString;

@ToString
@SuppressWarnings("unchecked")
public abstract class Class1 {
    protected int f1;

    public static class Builder<C extends Class1, B extends Builder<C, B>> {
        C obj;

        protected Builder(C constructedObj) {
            this.obj = constructedObj;
        }

        B f1(int f1) {
            obj.f1 = f1;
            return (B)this;
        }

        C build() {
            return obj;
        }
    }
}

두 번째 수준

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class2 extends Class1 {
    protected int f2;

    public static class Builder<C extends Class2, B extends Builder<C, B>> extends Class1.Builder<C, B> {
        public Builder() {
            this((C) new Class2());
        }

        protected Builder(C obj) {
            super(obj);
        }

        B f2(int f2) {
            obj.f2 = f2;
            return (B)this;
        }
    }
}

세 번째 수준

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class3 extends Class2 {
    protected int f3;

    public static class Builder<C extends Class3, B extends Builder<C, B>> extends Class2.Builder<C, B> {
        public Builder() {
            this((C) new Class3());
        }

        protected Builder(C obj) {
            super(obj);
        }

        B f3(int f3) {
            obj.f3 = f3;
            return (B)this;
        }
    }
}

그리고 사용법의 예

public class Test {
    public static void main(String[] args) {
        Class2 b1 = new Class2.Builder<>().f1(1).f2(2).build();
        System.out.println(b1);
        Class2 b2 = new Class2.Builder<>().f2(2).f1(1).build();
        System.out.println(b2);

        Class3 c1 = new Class3.Builder<>().f1(1).f2(2).f3(3).build();
        System.out.println(c1);
        Class3 c2 = new Class3.Builder<>().f3(3).f1(1).f2(2).build();
        System.out.println(c2);
        Class3 c3 = new Class3.Builder<>().f3(3).f2(2).f1(1).build();
        System.out.println(c3);
        Class3 c4 = new Class3.Builder<>().f2(2).f3(3).f1(1).build();
        System.out.println(c4);
    }
}


빌더의 복사 생성자를 특징으로하는 조금 더 긴 버전 :

첫 번째 수준-부모 (잠재적으로 요약)

import lombok.ToString;

@ToString
@SuppressWarnings("unchecked")
public abstract class Class1 {
    protected int f1;

    public static class Builder<C extends Class1, B extends Builder<C, B>> {
        C obj;

        protected void setObj(C obj) {
            this.obj = obj;
        }

        protected void copy(C obj) {
            this.f1(obj.f1);
        }

        B f1(int f1) {
            obj.f1 = f1;
            return (B)this;
        }

        C build() {
            return obj;
        }
    }
}

두 번째 수준

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class2 extends Class1 {
    protected int f2;

    public static class Builder<C extends Class2, B extends Builder<C, B>> extends Class1.Builder<C, B> {
        public Builder() {
            setObj((C) new Class2());
        }

        public Builder(C obj) {
            this();
            copy(obj);
        }

        @Override
        protected void copy(C obj) {
            super.copy(obj);
            this.f2(obj.f2);
        }

        B f2(int f2) {
            obj.f2 = f2;
            return (B)this;
        }
    }
}

세 번째 수준

import lombok.ToString;

@ToString(callSuper=true)
@SuppressWarnings("unchecked")
public class Class3 extends Class2 {
    protected int f3;

    public static class Builder<C extends Class3, B extends Builder<C, B>> extends Class2.Builder<C, B> {
        public Builder() {
            setObj((C) new Class3());
        }

        public Builder(C obj) {
            this();
            copy(obj);
        }

        @Override
        protected void copy(C obj) {
            super.copy(obj);
            this.f3(obj.f3);
        }

        B f3(int f3) {
            obj.f3 = f3;
            return (B)this;
        }
    }
}

그리고 사용법의 예

public class Test {
    public static void main(String[] args) {
        Class3 c4 = new Class3.Builder<>().f2(2).f3(3).f1(1).build();
        System.out.println(c4);

        // Class3 builder copy
        Class3 c42 = new Class3.Builder<>(c4).f2(12).build();
        System.out.println(c42);
        Class3 c43 = new Class3.Builder<>(c42).f2(22).f1(11).build();
        System.out.println(c43);
        Class3 c44 = new Class3.Builder<>(c43).f3(13).f1(21).build();
        System.out.println(c44);
    }
}

2

꺾쇠 괄호 또는 세 개로 시선을 찌르고 싶지 않거나 아마도 당신을 느끼지 않으려면 ... 음 ... 내 말은 ... 기침 ... 나머지 팀은 빨리 호기심을 이해할 것입니다 되풀이되는 제네릭 패턴, 다음을 수행 할 수 있습니다.

public class TestInheritanceBuilder {
  public static void main(String[] args) {
    SubType.Builder builder = new SubType.Builder();
    builder.withFoo("FOO").withBar("BAR").withBaz("BAZ");
    SubType st = builder.build();
    System.out.println(st.toString());
    builder.withFoo("BOOM!").withBar("not getting here").withBaz("or here");
  }
}

에 의해 지원

public class SubType extends ParentType {
  String baz;
  protected SubType() {}

  public static class Builder extends ParentType.Builder {
    private SubType object = new SubType();

    public Builder withBaz(String baz) {
      getObject().baz = baz;
      return this;
    }

    public Builder withBar(String bar) {
      super.withBar(bar);
      return this;
    }

    public Builder withFoo(String foo) {
      super.withFoo(foo);
      return this;
    }

    public SubType build() {
      // or clone or copy constructor if you want to stamp out multiple instances...
      SubType tmp = getObject();
      setObject(new SubType());
      return tmp;
    }

    protected SubType getObject() {
      return object;
    }

    private void setObject(SubType object) {
      this.object = object;
    }
  }

  public String toString() {
    return "SubType2{" +
        "baz='" + baz + '\'' +
        "} " + super.toString();
  }
}

부모 유형 :

public class ParentType {
  String foo;
  String bar;

  protected ParentType() {}

  public static class Builder {
    private ParentType object = new ParentType();

    public ParentType object() {
      return getObject();
    }

    public Builder withFoo(String foo) {
      if (!"foo".equalsIgnoreCase(foo)) throw new IllegalArgumentException();
      getObject().foo = foo;
      return this;
    }

    public Builder withBar(String bar) {
      getObject().bar = bar;
      return this;
    }

    protected ParentType getObject() {
      return object;
    }

    private void setObject(ParentType object) {
      this.object = object;
    }

    public ParentType build() {
      // or clone or copy constructor if you want to stamp out multiple instances...
      ParentType tmp = getObject();
      setObject(new ParentType());
      return tmp;
    }
  }

  public String toString() {
    return "ParentType2{" +
        "foo='" + foo + '\'' +
        ", bar='" + bar + '\'' +
        '}';
  }
}

키 포인트:

  • 상속으로 인해 상위 유형으로 보유 된 오브젝트의 필드를 설정할 수 없도록 빌더에서 오브젝트를 캡슐화하십시오.
  • 수퍼 유형 작성기 메소드에 추가 된 논리 (있는 경우)가 하위 유형에 유지되도록 수퍼 호출.
  • 단점은 부모 클래스에서 가짜 객체 생성입니다 ...하지만 그것을 정리하는 방법은 아래를 참조하십시오
  • 업사이드는 한 눈에 이해하기가 훨씬 쉬우 며 자세한 생성자 전송 속성은 없습니다.
  • 빌더 객체에 액세스하는 여러 스레드가있는 경우 ... 당신이 아니기 때문에 기쁩니다.

편집하다:

가짜 객체 생성을 둘러싼 방법을 찾았습니다. 먼저 이것을 각 빌더에 추가하십시오.

private Class whoAmI() {
  return new Object(){}.getClass().getEnclosingMethod().getDeclaringClass();
}

그런 다음 각 빌더의 생성자에서 :

  if (whoAmI() == this.getClass()) {
    this.obj = new ObjectToBuild();
  }

비용은 new Object(){}익명의 내부 클래스에 대한 추가 클래스 파일입니다.


1

할 수있는 한 가지 방법은 각 클래스에서 정적 팩토리 메소드를 작성하는 것입니다.

NutritionFacts.newBuilder()
GMOFacts.newBuilder()

이 정적 팩토리 메소드는 적절한 빌더를 리턴합니다. GMOFacts.Builder확장을 가질 수 있지만 NutritionFacts.Builder문제는 아닙니다. 여기서 문제는 가시성을 다루는 것입니다 ...


0

다음 IEEE 기여 Java의 Refined Fluent Builder는 이 문제에 대한 포괄적 인 솔루션을 제공합니다.

원래의 질문을 상속 결함유사 불변 의 두 가지 하위 문제로 분해 하고이 두 가지 하위 문제에 대한 솔루션이 Java의 클래식 빌더 패턴에서 코드 재사용으로 상속 지원을 위해 어떻게 열리는 지 보여줍니다.


이 답변에는 도움이되는 정보가 포함되어 있지 않으며, 링크에 제공된 답변에 대한 요약이 포함되어 있지 않으며 로그인이 필요한 링크로 연결됩니다.
소나타

이 답변은 공식 출판 기관과 공식 출판 및 공유 절차를 통해 동료 검토 회의 간행물에 연결됩니다.
mc00x1

0

두 가지 형식 유형 매개 변수를 허용하는 상위 추상 일반 빌더 클래스를 작성했습니다. 첫 번째는 build ()에 의해 리턴 된 객체의 유형이고, 두 번째는 각 선택적 매개 변수 설정기에 의해 리턴 된 유형입니다. 아래는 설명을위한 부모 및 자식 클래스입니다.

// **Parent**
public abstract static class Builder<T, U extends Builder<T, U>> {
    // Required parameters
    private final String name;

    // Optional parameters
    private List<String> outputFields = null;


    public Builder(String pName) {
        name = pName;
    }

    public U outputFields(List<String> pOutFlds) {
        outputFields = new ArrayList<>(pOutFlds);
        return getThis();
    }


    /**
     * This helps avoid "unchecked warning", which would forces to cast to "T" in each of the optional
     * parameter setters..
     * @return
     */
    abstract U getThis();

    public abstract T build();



    /*
     * Getters
     */
    public String getName() {
        return name;
    }
}

 // **Child**
 public static class Builder extends AbstractRule.Builder<ContextAugmentingRule, ContextAugmentingRule.Builder> {
    // Required parameters
    private final Map<String, Object> nameValuePairsToAdd;

    // Optional parameters
    private String fooBar;


    Builder(String pName, Map<String, String> pNameValPairs) {
        super(pName);
        /**
         * Must do this, in case client code (I.e. JavaScript) is re-using
         * the passed in for multiple purposes. Doing {@link Collections#unmodifiableMap(Map)}
         * won't caught it, because the backing Map passed by client prior to wrapping in
         * unmodifiable Map can still be modified.
         */
        nameValuePairsToAdd = new HashMap<>(pNameValPairs);
    }

    public Builder fooBar(String pStr) {
        fooBar = pStr;
        return this;
    }


    @Override
    public ContextAugmentingRule build() {
        try {
            Rule r = new ContextAugmentingRule(this);
            storeInRuleByNameCache(r);
            return (ContextAugmentingRule) r;
        } catch (RuleException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    Builder getThis() {
        return this;
    }
}

이것은 나의 만족을위한 나의 요구를 충족시켰다.

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