Java

FunctionalInterface, Lambda Expression

blackbearwow 2024. 9. 26. 12:41

ArrayList의 forEach, removeIf, sort 메소드 등 많은 곳에서 FunctionalInterface를 매개변수로 받고 있다. FunctionalInterface는 무엇인지 람다식은 무엇인지 알아보자.

FunctionalInterface란?

https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/FunctionalInterface.html 에서 보면, 디폴트 메소드를 제외한 abstract method가 하나인 인터페이스라고 말한다.

 

인터페이스 위에 @FunctionalInterface 어노테이션을 사용하는데, 이 어노테이션을 사용하면 인터페이스가 FunctionalInterface의 조건에 맞는지 컴파일러가 확인해준다. 어노테이션을 붙이지 않아도 동작에 상관이 없지만, 잘못될 경우가 있으니 사용하는 것이 좋다.

 

커스텀 FunctionalInterface 예시)

class Main {
    public static void main(String[] args) {
        Sum s = (a, b) -> (a + b);
        for (int i = 0; i < 10; i++) {
            System.err.println(s.oneMethod(i, 2));
        }
    }

    @FunctionalInterface
    public interface Sum {
        int oneMethod(int a, int b);
    }
}

 

FunctionalInterface는 lambda expression과 method reference의 타깃이다. 그렇다면 lambda expression과 method reference는 무엇인가?

Lambda expression

람다식은 Java8에 추가된 것으로 간단한 코드 블럭이다.

Lambda 문법

() -> {code block}
(parameter1) -> {code block}
(parameter1, parameter2) -> {code block}

() -> expression
(parameter1) -> expression
(parameter1, parameter2) -> expression

parameter1 -> expression
parameter1 -> {code block}

()안에 매개변수를 넣으며, 매개변수가 1개일 경우에만 괄호를 생략할 수 있다. 

중간에 ->가 있다.

{}에 code block을 넣을 수 있다. 괄호를 생략할 경우에는 expression 값이 반환된다. 생략한다면 변수, 할당, 조건문, 반복문을 사용하지 못하며 즉시 값을 반환해야 한다.

lambda expression의 타겟은 FunctionalInterface여야만 한다.

 

예시)

import java.util.ArrayList;

class Main {
    public static void main(String[] args) {
        ArrayList<Integer> arr = new ArrayList<>();
        Sum s = (a, b) -> (a + b);
        for (int i = 0; i < 10; i++) {
            arr.add(s.get(i, 2));
        }
        arr.forEach((n) -> {
            for (int i = 0; i < n; i++)
                System.out.print('*');
            System.err.println();
        });
    }

    @FunctionalInterface
    public interface Sum {
        int get(int a, int b);
    }
}

출력: 

**
***
****
*****
******
*******
********
*********
**********
***********

Method reference

메소드 참조는 메소드를 FunctionalInterface로 반환하는 방법이다. 일반 메소드, static 메소드, 생성자를 참조할 수 있으며 클래스이름::메소드이름 으로 참조한다.

 

이때 FunctionalInterface의 매개변수 타입 == 메소드의 매개변수타입, FunctionalInterface의 매개변수 개수 == 메소드의 매개변수 개수, 함수형 인터페이스의 반환형 == 메소드의 반환형 3가지 조건을 만족시켜야 한다.

 

예시)

import java.util.ArrayList;

class Main {
    public static void main(String[] args) {
        ArrayList<Integer> arr = new ArrayList<>();
        Sum s = (a, b) -> (a + b);
        for (int i = 0; i < 10; i++) {
            arr.add(s.get(i, 2));
        }
        arr.forEach(System.out::println);
    }

    @FunctionalInterface
    public interface Sum {
        int get(int a, int b);
    }
}

출력:

2

3

4

5

6

7

8

9

10

11

java.util.function

JDK에서 사용되는 일반적인 목적의 함수형 인터페이스의 모음 package이다. ArrayList, ArrayDeque, LinkedList 등에서 사용된다.

 

4가지 기본적인 함수형 인터페이스가 있다.

인터페이스 설명
Supplier<T> 매개변수 없이 반환값만 있다
Consumer<T> 단항의 매개변수를 받고 반환값이 없다
Predicate<T> 단항의 매개변수를 받고 Boolean을 반환한다
Function<T, R> 단항의 매개변수를 받아 처리후 R로 반환한다

위 인터페이스 외에 Bi가 붙은 인터페이스(BiConsumer, BiPredicate, BiFunction 등)는 2항의 매개변수를 받는 인터페이스이다.


참고: https://mangkyu.tistory.com/113

https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/FunctionalInterface.html

https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/function/package-summary.html

https://mangkyu.tistory.com/113

-