자바8 lambda expression 의 장점
- 코드 라인 수 감소 및 명확한 의도 전달
List<String> names = Arrays.asList("John", "Phil");
userName.stream().filter(name -> name.startsWith("P")).collect(Collectors.toList());
한 줄의 함축적 코드지만 의도를 쉽게 이해할 수 있음
- 병렬 처리 (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을 공유하게 되므로 성능상 이슈가 없는지 반드시 테스트가 필요
- 일급 객체로서 사용 가능
람다식으로 전달 할 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에선 친절하게 미리 정의된 함수형 인터페이스들을 제공함
- Function
T 타입의 파라미터를 받아서 R 타입의 결과를 반환
Function<Integer, String> intToString = i -> i + "";
- Supplier
파라미터는 없고 결과만 반환
Someclass someclass = new SomeClass();
Supplier<Double> lazyValue = () -> {
int a = someClass.method();
return a + 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"));
- 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)