olrlobt

[Java] 람다식, 함수형 인터페이스와 메소드 참조(::) 본문

Java/Java

[Java] 람다식, 함수형 인터페이스와 메소드 참조(::)

olrlobt 2023. 7. 14. 23:49

람다식 (Lambda Expression)

자바 8부터 람다식(lambda expression)이 도입되었다.

람다식은 자바에서도 익명 함수(anonymous function)를 표현하기 위한 간결하고 효율적인 방법으로,

함수형 인터페이스(functional interface)를 구현하기 위해 사용되며, 주로 함수형 프로그래밍과 스트림 처리(stream processing)에서 유용하게 쓰인다.

 

자바에서 람다식은 Stream과 Optional에 자주 쓰이며, 이 클래스들을 사용을 안 했더라도, 자바스크립트에서 자주 접했을 것이다.

 

 

Optional ex)

Optional<String> name = Optional.ofNullable(getName());
name.ifPresent(n -> System.out.println(n));

Stream ex)

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().forEach(n -> System.out.println(n));

Java Script ex)

element.addEventListener('click', () => {
  console.log('Clicked!');
});

 

위 예제처럼 함수, 특히 컬렉션 자료구조에서 유용하게 사용되며, 코드를 간결하고 가독성 있게 처리할 수 있다는 장점이 있다.

 


함수형 인터페이스 (Functional Interface)

함수형 인터페이스(Functional Interface)는 람다식을 지원하기 위해 도입된 개념이다.

함수형 인터페이스는 단 하나의 추상 메서드만을 가지는 인터페이스를 말한다. 이러한 특성 때문에 함수형 인터페이스는 람다식과 함께 사용될 수 있으며, 함수를 값으로 다룰 수 있는 기능을 제공한다.

 

 

함수형 인터페이스는 java.util.function 패키지에 내장되어 있으며, 대표적인 주요 함수형 인터페이스는 다음과 같다.

  1. Supplier <T>: 매개변수를 받지 않고, T 타입의 값을 제공하는 함수형 인터페이스
  2. Consumer <T>: T 타입의 값을 받아서 소비하는 함수형 인터페이스
  3. Function <T, R>: T 타입의 값을 입력으로 받아서 R 타입의 값을 반환하는 함수형 인터페이스
  4. Predicate <T>: T 타입의 값을 입력으로 받아서 boolean 값을 반환하는 함수형 인터페이스
  5. BinaryOperator <T>: 두 개의 입력 값을 받아서 같은 타입의 결과 값을 반환하는 함수. 입력과 출력의 타입이 동일

 

사용 방법 :

import java.util.function.BinaryOperator;
import java.util.function.Predicate;

public class example {
    public static void main(String[] args) {

        Predicate<Integer> isPositive = x -> x > 0;
        boolean result = isPositive.test(5);
        System.out.println(result); // true
        
        BinaryOperator<Integer> sum = (x, y) -> x + y;
        int result1 = sum.apply(2, 3); 
        System.out.println(result1); // 5
    }
}

 


@FunctionalInterface

@FunctionalInterface는 컴파일러에게 해당 인터페이스가 함수형 인터페이스임을 알려주는 역할을 한다.

앞서 예제에서 사용한 인터페이스들 역시, 패키지 내부를 보면 @FunctionalInterface를 사용한 것을 알 수 있다.

 

 

따라서, @FunctionalInterface를 이용하여 사용자가 함수형 인터페이스를 생성, 사용할 수 있다.

@FunctionalInterface
interface SumLamda{
    int sum(int a, int b);
}

public class example {
    public static void main(String[] args) {
        SumLamda sumLamda = (a, b) -> a + b;
        System.out.println(sumLamda.sum(3,5)); //8
    }
}

메소드 참조(::)

메소드 참조(::)는 자바에서 메서드를 간결하게 참조하고 호출하는 기능을 제공하는 표현 방식이다. 메소드 참조를 사용하면 람다식보다 더 간결한 코드를 작성할 수 있으며, 코드의 가독성을 높일 수 있다.

 

 

예를 들어, 자주 사용하는 Math 클래스의 sqrt 메소드를 참조해 보자.

public class example {
    public static void main(String[] args) {

        Function<Double, Double> sqrtFx = Math::sqrt;
        double result = sqrtFx.apply(16.0); // 4.0

        System.out.println(result);
    }
}

 

Function 함수형 인터페이스의 경우, 첫 번째 인자값 형식을 받아서 두 번째 인자값으로 반환해 준다.

또한, 앞서 설명했듯이 함수형 인터페이스에는 하나의 메소드만 존재해야 하기 때문에,

선언해 준 sqrtFx에 Math클래스의 sqrt 메소드 하나만 대입해 준 것을 볼 수 있다.

 

apply()는 Fuction에서 반환해 주는 메소드이다.

 

 

또한, 메소드 참조를 이용하면 컬렉션 사용에 유용해진다.

대표적으로 List의 값들을 출력한다고 생각해 보자.

 

평소에 List의 값을 반복문을 통하여 출력하곤 했다.

public class example {
    public static void main(String[] args) {
        List<Integer> list = List.of(1,2,3,4);
        for(int i : list){
        	System.out.print(i);
        }
    }
}

 

이를 메소드 참조로 변경하면,

public class example {
    public static void main(String[] args) {
        List<Integer> list = List.of(1,2,3,4);
        list.forEach(System.out::print);
    }
}

훨씬 간결해지고, 한층 더 개발자의 의도가 잘 드러나는 코드가 된다.

 

 

 

Comments