Chapter 01-02. 자바 8과 동작 파라미터화

Chapter 01-02. 자바 8과 동작 파라미터화

1. 자바 8의 핵심 변화

자바 역사상 가장 큰 변화가 자바 8에서 일어났다.

두 가지 핵심 목표

목표해결책
간결한 코드람다, 메서드 참조
멀티코어 활용스트림 API

2. 자바 8의 세 가지 핵심 개념

2.1 스트림 처리 (Stream Processing)

기존: 한 번에 한 항목 처리 (외부 반복)
Java 8: 고수준 추상화로 일련의 스트림 처리 (내부 반복)

핵심 장점:

  • 파이프라인 방식으로 데이터 처리
  • 멀티코어 병렬 처리 자동화
  • synchronized 없이 안전한 병렬 처리

2.2 동작 파라미터화 (Behavior Parameterization)

코드를 메서드의 인수로 전달하는 기법

// 메서드 참조로 동작 전달
filterApples(inventory, Apple::isGreenApple);

// 람다로 동작 전달
filterApples(inventory, (Apple a) -> a.getWeight() > 150);

2.3 병렬성과 공유 가변 데이터

안전한 병렬 처리 조건:

  • 공유된 가변 데이터(shared mutable data)에 접근하지 않기
  • 순수 함수 (pure function): 부작용 없는 함수
  • 상태 없는 함수 (stateless function)

3. 함수를 값으로 (일급 시민)

일급 시민 vs 이급 시민

구분설명예시
일급 시민자유롭게 전달 가능기본값, 객체
이급 시민전달 불가메서드, 클래스 (Java 7 이전)

Java 8: 메서드와 람다를 일급 시민으로 승격

메서드 참조 (Method Reference)

// Java 7: 익명 클래스
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
    public boolean accept(File file) {
        return file.isHidden();
    }
});

// Java 8: 메서드 참조
File[] hiddenFiles = new File(".").listFiles(File::isHidden);

:: = “이 메서드를 값으로 사용하라”

람다 (익명 함수)

// 한 번만 사용할 동작은 람다로 간결하게
filterApples(inventory, (Apple a) -> GREEN.equals(a.getColor()));
filterApples(inventory, (Apple a) -> a.getWeight() > 150);
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || RED.equals(a.getColor()));

4. 스트림 API

외부 반복 vs 내부 반복

// 외부 반복 (for-each)
for (Apple apple : inventory) {
    if (apple.getColor() == GREEN) {
        result.add(apple);
    }
}

// 내부 반복 (스트림)
inventory.stream()
         .filter(a -> a.getColor() == GREEN)
         .collect(toList());

컬렉션 vs 스트림

구분중점
컬렉션데이터 저장/접근 방법
스트림데이터 계산/처리 방법

포킹 (Forking) - 병렬 처리

┌──────────────────────────────────────┐
│           원본 리스트                  │
└──────────────────────────────────────┘
                  │
        ┌─────────┴─────────┐
        ▼                   ▼
┌───────────────┐   ┌───────────────┐
│   CPU 1       │   │   CPU 2       │
│  (앞부분)      │   │  (뒷부분)      │
└───────────────┘   └───────────────┘
        │                   │
        └─────────┬─────────┘
                  ▼
┌──────────────────────────────────────┐
│           결과 병합                   │
└──────────────────────────────────────┘

5. 디폴트 메서드

인터페이스에 구현부가 있는 메서드 추가 가능

public interface List<E> {
    // 기존 추상 메서드들...

    // 디폴트 메서드 (Java 8+)
    default void sort(Comparator<? super E> c) {
        Collections.sort(this, c);
    }
}

장점: 기존 구현 클래스를 수정하지 않고 인터페이스 확장 가능


6. 동작 파라미터화 상세

문제: 변화하는 요구사항

// 1차: 녹색 사과 필터링
filterGreenApples(inventory);

// 2차: 빨간 사과도 필터링 → 메서드 복붙? DRY 위반!
filterRedApples(inventory);

// 3차: 무게로 필터링 → 또 복붙?
filterHeavyApples(inventory);

해결: 동작 파라미터화

┌─────────────────────────────────────────┐
│          ApplePredicate (인터페이스)     │
│  boolean test(Apple apple)              │
└─────────────────────────────────────────┘
                    △
        ┌───────────┼───────────┐
        │           │           │
┌───────┴───────┐ ┌─┴─┐ ┌───────┴───────┐
│ GreenColor    │ │...│ │ HeavyWeight   │
│ Predicate     │ │   │ │ Predicate     │
└───────────────┘ └───┘ └───────────────┘

전략 패턴 (Strategy Pattern): 알고리즘을 캡슐화하고 런타임에 선택

진화 과정

단계방식코드량
1값 파라미터화메서드 중복
2인터페이스 + 구현 클래스클래스 파일 증가
3익명 클래스여전히 장황함
4람다간결함

최종 형태: 제네릭 + 람다

// 제네릭 Predicate 인터페이스
public interface Predicate<T> {
    boolean test(T t);
}

// 제네릭 filter 메서드
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

// 사용: 어떤 타입이든 필터링 가능
filter(apples, (Apple a) -> RED.equals(a.getColor()));
filter(numbers, (Integer n) -> n % 2 == 0);
filter(strings, (String s) -> s.length() > 5);

7. 프레디케이트 (Predicate)

인수를 받아 boolean을 반환하는 함수

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

사용 예:

Predicate<Apple> greenApple = a -> GREEN.equals(a.getColor());
Predicate<Apple> heavyApple = a -> a.getWeight() > 150;
Predicate<Integer> isEven = n -> n % 2 == 0;

요약

개념핵심
스트림내부 반복, 병렬 처리 자동화
동작 파라미터화코드를 메서드 인수로 전달
람다익명 함수, 간결한 코드
메서드 참조클래스::메서드로 메서드를 값처럼 전달
일급 시민함수도 값처럼 전달 가능
디폴트 메서드인터페이스에 구현 추가 가능
함수형 프로그래밍순수 함수, 부작용 없음, 상태 없음

Java 8 이전 vs 이후

// Before: 익명 클래스 (장황함)
Collections.sort(inventory, new Comparator<Apple>() {
    public int compare(Apple a1, Apple a2) {
        return a1.getWeight().compareTo(a2.getWeight());
    }
});

// After: 람다 + 메서드 참조 (간결함)
inventory.sort(comparing(Apple::getWeight));