JAVA/자바공부

JAVA 함수형 프로그래밍 (1) - Functional Interface

sendkite 2024. 8. 16. 03:31

 

  • 자바에서 제공하는 함수형 프로그래밍 도구 java.util.function 패키지에 위치한 대표적인 인터페이스 사용법을 정리한다.
  • 아래의 기능을 적재적소에서 활용하면 가독성, 유지보수, 확장성이 좋은 코드를 작성할 수 있다. 
  • 엄청 많은데 큰 분류로, Function, Functional Interface, Consumer, Supplier, Predicate, Comparator의 사용법을 알면 원시타입용도 인터페이스(박싱 안해서 메모리 덜 사용), 추가 매개변수용도 인터페이스와 같이 구분할 수 있다.

java.util.function

 

1. Function<T, R> 인터페이스

- 매개변수를 조작해서 반환값을 얻는 용도

@Test
@DisplayName("Function interface 테스트 - 매개변수가 1개")
void step1() {

    // Function<T, R> - T를 받아서 R을 리턴하는 함수형 인터페이스
    Function<Integer, Integer> adder = new Adder();
    assertThat(adder.apply(10)).isEqualTo(20);
}

class Adder implements Function<Integer, Integer> {

    @Override
    public Integer apply(Integer integer) {
        return integer + 10;
    }
}

 

2. Lambda Expression 

- 익명함수를 사용하면 가독성 있고 확장성 있게 쓸 수 있다.

@Test
@DisplayName("Lambda expression 테스트")
void step2() {

    Function<Integer, Integer> adder = (Integer x) -> x + 10;
    assertThat(adder.apply(10)).isEqualTo(20);
}

 

4.  BiFunction<T, U , R> 인터페이스

- 매개변수가 2개인 경우 반환값 처리할때 활용

@Test
@DisplayName("BiFunction 테스트 - 매개변수가 2개")
void step3() {

    // 매개변수가 2개인 경우 - BiFunction<T, U, R> - T, U를 받아서 R을 리턴하는 함수형 인터페이스
    BiFunction<Integer, Integer, Integer> adder = (x, y) -> x + y;
    assertThat(adder.apply(10, 10)).isEqualTo(20);
}

 

4.  Functional Interface

- 매개변수가 2개 이상인 경우 Functional Interface를 만들어 함수형 프로그래밍을 할 수 있다.

- @FunctionalInterface란 단 1개의 abstract method를 가진 인터페이스를 말한다.

- 아래는 매개변수 3개의 Functional Interface의 예시

 

@Test
@DisplayName("Functional interface 테스트 - 매개변수가 3개")
void step4() {
    TriFunction<Integer, Integer, Integer, Integer> adder = (x, y, z) -> x + y + z;
    assertThat(adder.apply(10, 10, 10)).isEqualTo(30);
}

@FunctionalInterface // 단 1개의 abstract method 만 가지는 인터페이스
public interface TriFunction<T, U, V, R> {

    R apply(T t, U u, V v);
}

5.  Supplier

- 매개변수 1개를 받아서 아무것도 리턴하지 않는 void 타입의 함수형 인터페이스 

@Test
@DisplayName("Functional interface 테스트 - Supplier")
void step5() {

    // Supplier<T> - T를 리턴하는 함수형 인터페이스
    Supplier<String> supplier = () -> "Hello, World!";
    assertThat(supplier.get()).isEqualTo("Hello, World!");
}

 

6. Consumer

- 매개변수 1개를 받아서 아무것도 리턴하지 않는 void 타입의 함수형 인터페이스 

- 앞서 언급한 함수형 인터페이스는 매개변수로 전달하여 추가 작업을 해, 확장성 좋은 코드를 짤 수 있다.

@Test
@DisplayName("Functional interface 테스트 - Consumer")
void step6() {

    // Consumer<T> - T를 받아서 아무것도 리턴하지 않는 함수형 인터페이스
    Consumer<String> helloConsumer = (String s) -> System.out.println(s);
    helloConsumer.accept("Hello, World!");


    Consumer<Integer> integerPrinter = (Integer i) -> System.out.println("input: " + i);
    Consumer<Integer> temMultiplyPrinter = (Integer i) -> System.out.println(i * 10);
    // 다형성 활용 - Consumer<Integer> 전달해서 확장성 있는 코드 작성 가능
    process(List.of(1, 2, 3), temMultiplyPrinter);
}

static <T> void process(List<T> inputs, Consumer<T> processor) {
    for (T input : inputs) {
        processor.accept(input);
    }
}

7. BiConsumer

- 매개변수 2개를 받아서 아무것도 리턴하지 않는 void 타입의 함수형 인터페이스 

@Test
@DisplayName("Functional interface 테스트 - BiConsumer")
void step7() {

    // BiConsumer<T, U> - T, U를 받아서 아무것도 리턴하지 않는 함수형 인터페이스
     BiConsumer<Integer, Double> printer = (x, y) -> System.out.println("x: " + x + ", y: " + y);
     printer.accept(10, 10.0);
}

 

8. Predicate

- 매개변수 2개를 받아서 boolean 값을 리턴하는 함수형 인터페이스.

- QueryDsl의 BooleanBuiler를 쓸때의 그것 

@Test
@DisplayName("Functional interface 테스트 - Predicate")
void step8() {

    // Predicate<T> - T를 받아서 boolean 을 리턴하는 함수형 인터페이스
    Predicate<Integer> isEven = (Integer x) -> x % 2 == 0;
    assertThat(isEven.test(10)).isTrue();
    assertThat(isEven.test(11)).isFalse();

    // Predicate 기본 함수 사용
    Predicate<Integer> isPositive = (Integer x) -> x > 0;
    Predicate<Integer> isNegative = (Integer x) -> x < 0;
    Predicate<Integer> isZero = (Integer x) -> x == 0;

    assertThat(isPositive.and(isNegative).test(10)).isFalse();
    assertThat(isPositive.and(isNegative).test(-10)).isFalse();
    assertThat(isPositive.and(isNegative).test(0)).isFalse();

    assertThat(isPositive.or(isNegative).test(10)).isTrue();
    assertThat(isPositive.or(isNegative).test(-10)).isTrue();
    assertThat(isPositive.or(isNegative).test(0)).isFalse();

    assertThat(isPositive.negate().test(10)).isFalse();
    assertThat(isPositive.negate().test(-10)).isTrue();
    assertThat(isPositive.negate().test(0)).isTrue();

    assertThat(isZero.negate().test(10)).isTrue();
    assertThat(isZero.negate().test(-10)).isTrue();
    assertThat(isZero.negate().test(0)).isFalse();
}

 

9. Comparator

- 매개변수를 받아서 int를 리턴하는 함수형 인터페이스이다.

- a와 b 2개의 값을 비교해서 작으면 음수, 크면 양수를 리턴한다.

- 아래는 이름 비교를 사용한 정렬 구현이다.

@Test
@DisplayName("Functional interface 테스트 - Comparator")
void step9() {

    // Comparator<T> - T를 받아서 int 를 리턴하는 함수형 인터페이스
    // compare(T o1, T o2) - o1이 o2보다 작으면 음수, 같으면 0, 크면 양수 리턴
    // given
    User user1 = User.create("hello@hello.com", "hello12", "def");
    User user2 = User.create("hello@hello.com", "hello123", "abc");
    List<User> userList = Arrays.asList(user1, user2);
    assertThat(userList.get(0)).isEqualTo(user1);

    // when
    Collections.sort(userList, (u1, u2) -> u1.getName().compareTo(u2.getName()));

    // then
    assertThat(userList.get(0)).isEqualTo(user2);
    assertThat(userList.get(1)).isEqualTo(user1);
}
반응형