Java

Effective Java 제 7장 - 람다와 스트림

iksadnorth 2023. 8. 22. 23:05

해당 게시물은 책 'Effective Java'를 참고하고 작성했습니다.

가독성을 위해 익명 클래스보다 람다식을 사용해라!
만약 구현해야 하는 인터페이스의 메서드가 1개뿐이라면 익명 클래스와 람다식은 동일하다.
람다식은 최소의 표현으로 작동 방식에 대해 서술하므로 가독성이 좋고 Cost도 적다.
하지만 만약 코드 Line 수가 너무 많거나 코드에 대한 설명이 필요한 경우,
람다식보다 차라리 클래스 파일 하나를 더 만드는 것이 좋다.

익명 클래스
람다식

가독성을 위해 람다식보다 메서드 참조를 사용해라!
역시나 위 내용처럼 가독성을 위해 메서드 참조를 권장한다. 하지만 위 사례보다는 예외가 많기 때문에 가독성을 위해 두가지 선택지를 유동적으로 선택하면 된다.

람다식
메서드 참조

표준 함수형 인터페이스를 적극 활용하라!
표준 함수형 인터페이스란, java.util.function 패키지의 인터페이스를 일컫는 말로,
Function<T,U>, Predictor<T>, Supplier<T>, Consumer<T>, BiFunction<T,U,R> 등등이 있다.
물론 해당 인터페이스의 의도를 분명히 하기 위해
굳이 이름을 정의한 인터페이스를 정의해도 되지만 [Ex. Comparator<T>]
그럴 필요가 없다면 표준 함수형 인터페이스를 사용하는 것이 좋다.
만약 Comparator와 같이 직접 만든 함수형 인터페이스라도
@FunctionalInterface라는 어노테이션을 기입하는 습관을 들여야 한다. 
왜냐면 위 인터페이스는 람다식을 사용하기 위해 만든 인터페이스라 메서드가 1개뿐이여야 한다.
하지만 후임자가 이런 의도를 파악하지 못해 메서드를 추가하는 불상사가 벌어질 수 있으므로
@FunctionalInterface를 붙여 컴파일 단계에서 오류를 내게 해야 한다. 

Stream에 매몰되지 말고 가독성을 해치지 않는 선에서 사용하자!
모든 반복문을 Stream을 이용해서 처리하면 좋을 것 같지만 
무분별한 Stream은 가독성을 심하게 해치기 때문에 적당히 사용해야 한다.

무분별한 사용 예시

Stream의 forEach는 로그를 위해서만 사용하고 연산 결과를 계산하기 위해 사용하지 말라!
Stream의 내부 작동은 Lazy Evaluation을 활용하기에 연산 결과를 확실히 예측하기 어렵고
forEach의 경우, 스트림의 데이터를 순차적으로 처리하는 것이 아닌 때론 병렬적으로 처리하기도 한다.
때문에 스트림 연산으로 외부 변수의 상태변화를 일으키면 예기치 못한 문제를 일으킬 수 있다. 
게다가 이는 함수형 프로그래밍 철학을 무시하는 행위이기에
계산 결과는 항상 collect() 메서드로 연산해 처리해야 한다.

Return 값으로 최대한 Stream을 내뱉지 않도록 하자!
내용이 너무 난해해 2회독에서 더 자세히 분석하겠지만 
Stream으로 반환하면 iterable하지 못하기에 사용성에서 문제가 발생한다.
때문에 최대한 Stream으로 반환하지 말고 Collections으로 반환하는 것이 좋다.

stream의 병렬화를 무분별하게 사용하지 말자!
생각보다 스트림 파이프라인 병렬화는 제 기능을 발휘하기 어렵다.
자세한 제한 조건은 책에 안내되어 있다.
물론 조건만 맞아떨어지면 코어 갯수만큼 성능을 발휘할 수 있지만
조건이 맞지 않는다면 차라리 적용하지 않는 것이 나을 정도로 악화되므로 유의해라!