Java 람다식 매개변수 구현(with. 메소드 체이닝)

dhbyun·2023년 9월 20일

Java

목록 보기
1/2

개발 중에 특정 API의 응답값에 기반하여 각 메소드마다 다르게 처리해야 하는 상황이 발생했습니다. 메소드마다 응답 코드 분류는 동일했지만 처리 방법이 달랐습니다. 조건문을 사용하여 각 메소드 별로 구현하다가 Java의 스트림(Stream)이 떠올랐습니다. 개발을 하면서 자주 사용하는 map(), filter(), forEach()와 같이 람다식을 매개변수로 전달하고자 했습니다. 이를 어떻게 구현했는지 정리해보겠습니다.

Java Stream Sample

Arrays.stream(/* Array */).map(() -> {/* 가공 */})
		.filter(() -> /* 분류 */).foreach(() -> {/* 처리 */});

제가 참고한 예시입니다.


Method Chaining

Method Chaining(메소드 체이닝)이란 함수 호출 후 반환된 객체의 메소드를 이어서 호출하는 패턴을 말합니다.

public class TransferResultRVO {
	/** ... **/
    
    public TransferResultRVO onSuccess() {
    	/* 성공 처리 작업 */
    	return this;
    }
    
    public TransferResultRVO onPending() {
    	/* Pending 처리 작업 */
    	return this;
    }
    
    public TransferResultRVO onFailed() {
    	/* 실패 처리 작업 */
    	return this;
    }
    
    public void onLast() {
    	/* 마무리 작업 */
    }
}
void sampleFunction() {
	/** ... **/
    
	TransferResultRVO result = apiCall();
	result.onSuccess().onPending().onFailed().onLast();
    
    /** ... **/
}

응답 코드가 담기는 Class에 저에게 필요한 상황별로 객체를 반환하는 메소드를 추가했습니다.

Lambda Expression Parameters

Lambda Expression(람다식)이란 메소드를 하나의 '식(Expression)'으로 표현한 것을 말합니다.

  1. Runnable
@FunctionalInterface
public interface Runnable {
    /* () -> {} */
    public abstract void run();
}
  1. Consumer<T>
package java.util.function;

/** ... **/
@FunctionalInterface
public interface Consumer<T> {
	/* (t) -> {} */
    void accept(T t);
   
    /** ... **/
}
  1. Function<T, R>
package java.util.function;

/** ... **/
@FunctionalInterface
public interface Function<T, R> {
	/* (t) -> {return r} */
    R apply(T t);
   
    /** ... **/
}

람다식을 매개변수로 받을 때 사용할 수 있는 인터페이스(interface) 입니다.
BiConsumer<T, U>, BiFunction<T, U, R> 등 다양한 인터페이스가 있지만,

  1. Runnable
  2. Consumer<T>

저는 위 두가지만 사용하였습니다.

@EqualsAndHashCode
@NoArgsConstructor
public class TransferResultRVO {
	/** ... **/
    
    private static final TransferResultRVO EMPTY = new TransferResultRVO();
    
    private boolean isValid() {
        return !this.equals(this.EMPTY);
    }
    
    public TransferResultRVO onSuccess(Runnable runnable) {
    	if (this.isValid() && /* 성공 조건 */) {
            runnable.run();
            return this.EMPTY;
        }

        return this;
    }
    
    public TransferResultRVO onSuccess(Consumer<TransferResultRVO> consumer) {
    	if (this.isValid() && /* 성공 조건 */) {
            consumer.accept(this);
            return this.EMPTY;
        }

        return this;
    }
    
    public TransferResultRVO onPending(Runnable runnable) {
    	if (this.isValid() && /* Pending 조건 */) {
            runnable.run();
            return this.EMPTY;
        }

        return this;
    }
    
    public TransferResultRVO onPending(Consumer<TransferResultRVO> consumer) {
    	if (this.isValid() && /* Pending 조건 */) {
            consumer.accept(this);
            return this.EMPTY;
        }

        return this;
    }
    
    public TransferResultRVO onFailed(Runnable runnable) {
    	if (this.isValid() && /* 실패 조건 */) {
            runnable.run();
            return this.EMPTY;
        }

        return this;
    }
    
    public TransferResultRVO onFailed(Consumer<TransferResultRVO> consumer) {
    	if (this.isValid() && /* 실패 조건 */) {
            consumer.accept(this);
            return this.EMPTY;
        }

        return this;
    }
    
    public void onLast(Runnable runnable) {
    	if (this.isValid()) {
            runnable.run();
        }
    }
    
    public void onLast(Consumer<TransferResultRVO> consumer) {
    	if (this.isValid()) {
            consumer.accept(this);
        }
    }
}

작업내용

  1. Parameter 추가(Runnable, Consumer<T>) + Overload(오버로드)
  2. isValid() 메소드를 이용하여 onSuccess(), onPending(), onFailed() 작업 진행 후
    비어있는 Class(EMPTY)를 반환하여 다음 호출되는 메소드 수행 방지 (Optional.orElse() 참고)
void sampleFunction() {
	/** ... **/
    
	TransferResultRVO result = apiCall();
	result.onSuccess((result) -> {
    	/* 성공 처리 작업 */
    }).onPending((result) -> {
    	/* Pending 처리 작업 */
    }).onFailed((result) -> {
    	/* 실패 처리 작업 */
    }).onLast((result) -> {
    	/* 처리되지 못할 경우 마무리 작업 */
    });
    
    /** ... **/
}

마무리

제가 구현한 방법을 정리한 것이며, 올바르지 못한 내용이 있을 수 있습니다.
참고하는 정도로만 봐주시고 수정이 필요한 부분이 있다면 말씀 부탁드립니다.

감사합니다.

profile
어제보다 더 발전하는 오늘

0개의 댓글