ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JAVA 함수형 프로그래밍 (1) - Functional Interface
    JAVA/자바공부 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);
    }
    반응형
Designed by Tistory.