1. 소개

Java 8부터 우리는 Java에서 하나 또는 두 개의 매개변수 함수를 정의할 수 있으므로 매개변수로 전달하여 해당 동작을 다른 함수에 주입할 수 있습니다. 그러나 매개변수가 더 많은 함수의 경우 Vavr 과 같은 외부 라이브러리에 의존합니다 .

또 다른 옵션은 커링을 사용하는 것입니다 . 커링과 기능적 인터페이스를 결합하여 사용자가 모든 입력을 제공하도록 하는 읽기 쉬운 빌더를 정의할 수도 있습니다.

이 예제에서는 커링을 정의하고 사용법을 제시합니다 .

2. 간단한 예

여러 매개변수가 있는 문자의 구체적인 예를 살펴보겠습니다.

간소화된 첫 번째 버전에는 본문과 인사말만 필요합니다.

class Letter {
    private String salutation;
    private String body;
    
    Letter(String salutation, String body){
        this.salutation = salutation;
        this.body = body;
    }
}

2.1. 방법에 의한 생성

이러한 객체는 다음 메서드를 사용하여 쉽게 만들 수 있습니다.

Letter createLetter(String salutation, String body){
    return new Letter(salutation, body);
}

2.2. BiFunction 으로 생성

위의 방법은 잘 작동하지만 함수형 스타일로 작성된 항목에 이 동작을 제공해야 할 수도 있습니다. Java 8부터 이러한 목적으로 BiFunction을 사용할 수 있습니다.

BiFunction<String, String, Letter> SIMPLE_LETTER_CREATOR 
  = (salutation, body) -> new Letter(salutation, body);

2.3. 일련의 함수로 생성

우리는 또한 이것을 각각 하나의 매개변수를 가진 일련의 함수로 다시 말할 수 있습니다:

Function<String, Function<String, Letter>> SIMPLE_CURRIED_LETTER_CREATOR 
  = salutation -> body -> new Letter(salutation, body);

인사말이 함수에 매핑되는 것을 볼 수 있습니다 . 결과 함수는 새 Letter 개체에 매핑됩니다. 반환 유형이 BiFunction 에서 어떻게 변경되었는지 확인하십시오 . 우리는 Function 클래스 만 사용하고 있습니다 . 이러한 일련의 함수로의 변환을 커링(currying)이라고 합니다.

3. 고급 예제

커링의 이점을 보여주기 위해 Letter 클래스 생성자를 더 많은 매개변수로 확장해 보겠습니다.

class Letter {
    private String returningAddress;
    private String insideAddress;
    private LocalDate dateOfLetter;
    private String salutation;
    private String body;
    private String closing;

    Letter(String returningAddress, String insideAddress, LocalDate dateOfLetter, 
      String salutation, String body, String closing) {
        this.returningAddress = returningAddress;
        this.insideAddress = insideAddress;
        this.dateOfLetter = dateOfLetter;
        this.salutation = salutation;
        this.body = body;
        this.closing = closing;
    }
}

3.1. 방법에 의한 생성

이전과 마찬가지로 메서드를 사용하여 개체를 만들 수 있습니다.

Letter createLetter(String returnAddress, String insideAddress, LocalDate dateOfLetter, 
  String salutation, String body, String closing) {
    return new Letter(returnAddress, insideAddress, dateOfLetter, salutation, body, closing);
}

3.2. 랜덤의 인수에 대한 함수

Arity는 함수가 취하는 매개변수의 수를 측정한 것입니다. Java는 nullary ( Supplier ), unary ( Function ) 및 binary( BiFunction ) 에 대한 기존 기능 인터페이스를 제공 하지만 그게 전부입니다. 새로운 기능 인터페이스를 정의하지 않고는 6개의 입력 매개변수가 있는 함수를 제공할 수 없습니다.

Currying은 우리의 탈출구입니다. 랜덤의 인수를 단일 함수 시퀀스로 변환합니다 . 예를 들어 다음을 얻습니다.

Function<String, Function<String, Function<LocalDate, Function<String,
  Function<String, Function<String, Letter>>>>>> LETTER_CREATOR =
  returnAddress
    -> closing
    -> dateOfLetter
    -> insideAddress
    -> salutation
    -> body
    -> new Letter(returnAddress, insideAddress, dateOfLetter, salutation, body, closing);

3.3. 자세한 유형

분명히 위의 유형은 가독성이 좋지 않습니다. 이 양식에서는 '적용'을 여섯 번 사용하여 편지를 만듭니다 .

LETTER_CREATOR
  .apply(RETURNING_ADDRESS)
  .apply(CLOSING)
  .apply(DATE_OF_LETTER)
  .apply(INSIDE_ADDRESS)
  .apply(SALUTATION)
  .apply(BODY);

3.4. 사전 채우기 값

이 함수 체인을 사용하여 첫 번째 값을 미리 채우고 문자 개체의 후속 완성을 위해 함수를 반환하는 도우미를 만들 수 있습니다.

Function<String, Function<LocalDate, Function<String, Function<String, Function<String, Letter>>>>> 
  LETTER_CREATOR_PREFILLED = returningAddress -> LETTER_CREATOR.apply(returningAddress).apply(CLOSING);

이것이 유용하려면 원래 함수에서 매개변수의 순서를 신중하게 선택하여 덜 구체적인 것이 첫 번째 매개변수가 되도록 해야 합니다.

4. 빌더 패턴

친숙하지 않은 유형 정의와 표준 적용 방법 의 반복 사용을 극복하기 위해 올바른 입력 순서에 대한 단서가 없음을 의미하며 빌더 패턴을 사용할 수 있습니다 .

AddReturnAddress builder(){
    return returnAddress
      -> closing
      -> dateOfLetter
      -> insideAddress
      -> salutation
      -> body
      -> new Letter(returnAddress, insideAddress, dateOfLetter, salutation, body, closing);
}

일련의 기능 대신 일련의 기능 인터페이스를 사용합니다 . 위 정의의 반환 유형은 AddReturnAddress 입니다 . 다음에서는 중간 인터페이스만 정의하면 됩니다.

interface AddReturnAddress {
    Letter.AddClosing withReturnAddress(String returnAddress);
}
    
interface AddClosing {
    Letter.AddDateOfLetter withClosing(String closing);
}
    
interface AddDateOfLetter {
    Letter.AddInsideAddress withDateOfLetter(LocalDate dateOfLetter);
}

interface AddInsideAddress {
    Letter.AddSalutation withInsideAddress(String insideAddress);
}

interface AddSalutation {
    Letter.AddBody withSalutation(String salutation);
}

interface AddBody {
    Letter withBody(String body);
}

따라서 이것을 사용하여 편지를 만드는 것은 매우 자명합니다.

Letter.builder()
  .withReturnAddress(RETURNING_ADDRESS)
  .withClosing(CLOSING)
  .withDateOfLetter(DATE_OF_LETTER)
  .withInsideAddress(INSIDE_ADDRESS)
  .withSalutation(SALUTATION)
  .withBody(BODY));

이전과 마찬가지로 문자 객체를 미리 채울 수 있습니다.

AddDateOfLetter prefilledLetter = Letter.builder().
  withReturnAddress(RETURNING_ADDRESS).withClosing(CLOSING);

인터페이스는 채우기 순서를 보장 합니다 . 따라서 미리 채울 수 없습니다 .

5. 결론

커링을 적용하는 방법을 살펴보았으므로 표준 Java 기능 인터페이스에서 지원하는 제한된 수의 매개변수에 제약을 받지 않습니다. 또한 처음 몇 개의 매개변수를 쉽게 미리 채울 수 있습니다. 또한 이를 사용하여 읽기 쉬운 빌더를 만드는 방법을 배웠습니다.

항상 그렇듯이 전체 코드 샘플은 GitHub에서 사용할 수 있습니다 .

Generic footer banner