함수형 프로그래밍

순수 함수와 불변성을 기반으로, 상태 변경과 부수 효과를 최소화하여 프로그래밍하는 패러다임입니다.

핵심 개념

개념설명
순수 함수같은 입력 → 항상 같은 출력, 부수 효과 없음
불변성데이터를 변경하지 않고 새로운 데이터 반환
1급 함수함수를 변수에 할당, 인자로 전달, 반환 가능
고차 함수함수를 인자로 받거나 반환하는 함수

순수 함수

// X 순수 함수 아님 (외부 상태 변경)
int count = 0;
void increment() {
    count++;  // 부수 효과 발생
}

// O 순수 함수 (같은 입력 → 같은 출력, 외부 영향 없음)
int add(int a, int b) {
    return a + b;
}

불변성

// X 불변성 위반 (원본 변경)
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
list.add(4);  // 원본 변경

// O 불변성 유지 (새 리스트 반환)
List<Integer> list = List.of(1, 2, 3);
List<Integer> newList = Stream.concat(list.stream(), Stream.of(4))
                              .collect(Collectors.toList());

1급 함수 (함수형 인터페이스)

// Java에서는 함수형 인터페이스로 함수를 1급 객체처럼 사용
Function<Integer, Integer> double_ = x -> x * 2;
Function<Integer, Integer> addOne  = x -> x + 1;

// 변수에 할당
double_.apply(5);   // 10

// 인자로 전달
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.stream()
       .map(double_)  // 함수를 인자로 전달
       .collect(Collectors.toList());  // [2, 4, 6, 8, 10]

고차 함수 (Stream API)

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

// map - 변환
List<Integer> doubled = numbers.stream()
    .map(x -> x * 2)
    .collect(Collectors.toList());  // [2, 4, 6, 8, 10]

// filter - 필터링
List<Integer> evens = numbers.stream()
    .filter(x -> x % 2 == 0)
    .collect(Collectors.toList());  // [2, 4]

// reduce - 집계
int sum = numbers.stream()
    .reduce(0, Integer::sum);  // 15

함수 합성

Function<Integer, Integer> double_ = x -> x * 2;
Function<Integer, Integer> addOne  = x -> x + 1;

// andThen: double_ 실행 후 addOne 실행
Function<Integer, Integer> doubleThenAdd = double_.andThen(addOne);
doubleThenAdd.apply(3);  // 7 (3 * 2 + 1)

// compose: addOne 실행 후 double_ 실행
Function<Integer, Integer> addThenDouble = double_.compose(addOne);
addThenDouble.apply(3);  // 8 ((3 + 1) * 2)

커링

// Java에서 커링 구현
Function<Integer, Function<Integer, Integer>> add = a -> b -> a + b;

Function<Integer, Integer> add5 = add.apply(5);  // 부분 적용
add5.apply(3);   // 8
add5.apply(10);  // 15

명령형 vs 함수형

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

// 명령형 - 어떻게(How) 할지 작성
List<Integer> result = new ArrayList<>();
for (int n : numbers) {
    if (n % 2 == 0) {
        result.add(n * 2);
    }
}

// 함수형 - 무엇을(What) 할지 작성
List<Integer> result = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * 2)
    .collect(Collectors.toList());

장단점

장점단점
테스트 용이 (순수 함수)학습 곡선 높음
부수 효과 없어 예측 가능불변성으로 메모리 사용 증가
병렬 처리에 유리모든 상황에 적합하지 않음
코드 가독성 향상Java는 완전한 함수형 언어 아님

Java 8부터 Stream API, 람다, 함수형 인터페이스(Function, Predicate, Consumer 등) 가 도입되어 함수형 프로그래밍 스타일을 지원합니다.

CSLOG에서 보기