Java8 lambda expression

rory·2022년 7월 3일
0

자바8 lambda expression 의 장점

  1. 코드 라인 수 감소 및 명확한 의도 전달
List<String> names = Arrays.asList("John", "Phil");

userName.stream().filter(name -> name.startsWith("P")).collect(Collectors.toList());

한 줄의 함축적 코드지만 의도를 쉽게 이해할 수 있음

  1. 병렬 처리 (Stream API)
List<String> l1 = Arrays.asList("1", "2", "3");

l1.parallelStream().forEach(System.out::println);

ExecutorService 혹은 @Async 등의 추가 코드 없이 간단히 구현 가능. 특별한 설정이 없다면 내부적으로는 ForkJoinPool.commonPool() 를 사용

Thread Pool에 대한 설정은 ForkJoinPool을 사용해서 변경이 가능하지만, 이렇게 생성한 풀은 GC대상이 아니므로 반드시 finally 에서 .shutdowon() 으로 메모리 누수를 막아야 함.
(https://www.baeldung.com/java-8-parallel-streams-custom-threadpool)

ForkJoinPool pool = new ForkJoinPool(8);

try {
    long actualTotal = customThreadPool.submit(
      () -> aList.parallelStream().reduce(0L, Long::sum)).get();
} finally {
    customThreadPool.shutdown();
}

parallelStream() 을 사용할 때, 모든 Stream 객체가 각각의 Pool을 만드는 것이 아니라 하나의 Pool을 공유하게 되므로 성능상 이슈가 없는지 반드시 테스트가 필요

  1. 일급 객체로서 사용 가능

람다식으로 전달 할 functional interface 정의

@FunctionalInterface
public interface MySupplier<T> {
    T get() throws Exception;
}

정의된 functional interface를 메서드의 파라미터로 지정

public class MyClass {

    public void myMethod1(MySupplier<String> param) {

        try {
            System.out.println(param.get());
        } catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
        }
    }
}

functional interface를 파라미터로 받는 메서드에, 1급 객체로 사용이 가능한 익명 함수(람다식) 을 전달

public class Main {

    public static void main(String[] args) {

        MyClass myClass = new MyClass();
        myClass.myMethod1(() -> "my word");
        
        return;
    }

}

위 코드를 실행하면 "my word" 가 프린트됨.
myClass.myMethod1(() -> "my word"); 에서 람다식을 파라미터로 전달해서, myMethod1 내부에서 param.get() 이 실행될 때 실제로는 () -> "my word" 이 불려지게 됨.

코드 블럭의 평가(실행) 시점을 주의해서 보기.

@FunctionalInterface 란?

함수형 인터페이스는 '추상 메서드를 단 하나만 가진 인터페이스' 라고 정의할 수 있음. 위 어노테이션은 유저가 정의한 인터페이스가 함수형 인터페이스라고 명시함과 동시에, 조건에 부합하는지 체크해 주는 역할을 함

@FunctionalInterface
interface MyFunctionalInterface<T> {

    T theOnlyAbstractMethod();

    default void defaultMethod() {
        ...
    }

    static void staticMethod() {
        ...
    }
}

위 인터페이스는 default, static 메서드가 존재해도 abstract method가 단 하나이므로 함수형 인터페이스.

유저가 매번 인터페이스를 정의하기 귀찮을까봐, Java8에선 친절하게 미리 정의된 함수형 인터페이스들을 제공함

  1. Function

T 타입의 파라미터를 받아서 R 타입의 결과를 반환

Function<Integer, String> intToString = i -> i + "";
  1. Supplier

파라미터는 없고 결과만 반환

Someclass someclass = new SomeClass();

Supplier<Double> lazyValue = () -> {
    int a = someClass.method();
    return a + 1;
};
  1. Consumer

파라미터는 있지만, 리턴은 없음

Map<String, Integer> ages = new HashMap<>();
ages.put("John", 25);
ages.put("Freddy", 24);
ages.put("Samuel", 30);

ages.forEach((name, age) -> System.out.println(name + " is " + age + " years old"));
  1. Predicates

파라미터를 받아서 boolean 값을 반환

List<String> names = Arrays.asList("Angela", "Aaron", "Bob", "Claire", "David");

List<String> namesWithA = names.stream()
  .filter(name -> name.startsWith("A"))
  .collect(Collectors.toList());

이외에도 Callable, Runnable, Comparator, Operator 등의 인터페이스가 존재함.

람다식을 사용할 때의 팁과 best practice에 관한 글 참조.
(https://www.baeldung.com/java-8-lambda-expressions-tips)

profile
backend developer

0개의 댓글