Java

[Effective Java] 7-1. 람다와 스트림

noahkim_ 2024. 12. 31. 03:07

조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.


1. 익명 클래스보다는 람다를 사용하라

람다 함수

Collections.sort(words, new Comparable<String>() {
    public int compare(String s1, String s2) {
    	return Integer.compare(s1.length(), s2.length());
    }    
}
Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
  • 함수형 인터페이스의 인스턴스를 짧게 즉시 구현

 

장점
  • 간결함
  • 매개변수형의 타입 생략 가능
    • 컴파일러가 타입을 추론함
    • 직접 명시 가능

 

단점
  • 이름이 없어, 문서화 못함
  • 코드 자체로 동작이 명확히 설명되지 않거나 코드가 길어지면, 사용하지 않는것이 좋음
  • 열거타입 안의 람다는 인수들의 타입을 추론하지 못함
  • 자신을 참조하지 못함
    • this는 바깥 인스턴스를 가리킴
  • 직렬화 형태가 jvm 별로 다름 (사용 비권장)

 

2. 람다보다는 메서드 참조를 사용하라

메서드 참조

  • 람다보다 더 간결하고 읽기쉽게 메서드를 표현하는 방법

 

장점
  • 가독성
  • 이름을 지을 수 있음

 

정적 메서드 참조
List<String> names = List.of("Alice", "Bob", "Charlie");

// 람다 표현식
names.forEach(name -> System.out.println(name));

// 메서드 참조
names.forEach(System.out::println);

 

한정적 인스턴스 메서드 참조
MethodReferenceExample example = new MethodReferenceExample();

// 메서드 참조
Runnable r2 = example::print;

r1.run();  // Hello
  • 정적 참조와 비슷
  • 함수 객체가 받는 인수와 참조되는 메서드가 받는 인수가 똑같음

 

비한정적 인스턴스 메서드 참조
Function<String, Integer> lambda = s -> s.length();  // 람다 표현식
Function<String, Integer> methodRef = String::length;  // 메서드 참조

System.out.println(lambda.apply("Hello"));  // 5
System.out.println(methodRef.apply("Hello"));  // 5
  • 인스턴스를 받아 메서드 호출
  • 객체의 첫번째 인수가 참조되는 메서드의 수신 객체가 됨

 

3. 표준 함수형 인터페이스를 사용하라

함수형 인터페이스

  • 추상 메서드를 오직 하나만 가지고 있는 인터페이스

 

@FunctionalInterface
  • 마커 애노테이션

 

표준 함수형 인터페이스

  • 자바 표준 라이브러리에 자주 사용되는 모양의 인터페이스가 준비됨 (java.util.function)

 

장점
  • 유용한 디폴트 메서드 제공
  • 다른 코드와의 상호운용성

 

종류

Operator
  • 반환값과 인수의 타입이 같음
  • UnaryOperator: 인수가 1개
  • BinaryOperator: 인수가 2개

 

Function
  • 인수와 반환 타입이 다른 함수

 

Predicate
  • 인수를 하나를 받고. boolean 반환

 

Consumer
  • 인수를 하나 받고, 반환값이 없음

 

Supplier
  • 인수를 받지 않고, 값을 반환

 

전용 함수형 인터페이스 고려사항

  • 자주 쓰이며, 이름 자체가 용도를 정확히 설명해줌
  • 반드시 따라야 하는 규약 존재
  • 유용한 디폴트 메서드 제공