[JAVA] 함수형 프로그래밍

JUJU·2024년 5월 27일
0

✏️ 함수형 프로그래밍

자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임

우리에게 익숙한 절차지향, 객체지향 프로그래밍은 모두 명령형 프로그래밍 패러다임이다.

  • "HOW(어떻게)"에 초점을 맞춘 프로그래밍이다.
  • 프로그래머는 컴퓨터에게 명확한 명령의 집합을 제공한다.

함수형 프로그래밍은, 선언형 프로그래밍 패러다임의 일종이다.

  • ""WHAT(무엇을)""에 초점을 맞춘 프로그래밍이다.
  • 프로그램이 수행할 작업을 설명하지만, 구체적인 방법은 명시하지 않는다.

함수형 프로그래밍의 핵심은 side effects를 최소화하여 프로그램의 동작을 예측 가능하게 만드는 것이다.
side effects를 최소화하기 위해서는 상태 변경이나 변경 가능한 데이터를 피해야 한다.
➜ 순수 함수를 사용한다.


■ 특징

함수형 프로그래밍의 주요 특징은 다음과 같다.


특징설명
순수 함수같은 입력에 대해 항상 같은 출력 반환한다.
외부 상태를 변경하지 않는다.
불변성데이터는 한 번 생성되면 변경되지 않는다.
고차 함수함수를 다른 함수의 인자로 전달할 수 있다.
함수를 리턴할 수 있다.
함수 합성여러 함수를 조합하여 새로운 함수를 만들 수 있다.
게으른 평가필요할 때까지 계산을 지연시킨다.

■ 장점

함수형 프로그래밍의 장점은 다음과 같다.

  1. 부작용 최소화
  2. 코드의 모듈성 및 재사용성
  3. 향상된 테스트와 디버깅
  4. 병렬 처리의 용이성

■ JAVA와 함수형 프로그래밍

자바는 Java 8 버전부터 함수형 프로그래밍을 지원하기 위해 람다와 스트림이 도입되었다.




✏️ 람다

람다는 메소드를 하나의 식으로 표현한 것이다.

  • 람다식은 익명 함수이다.
    • 더 정확히 말하자면 익명 클래스이다.
  • 함수형 인터페이스를 구현하여 생성된다.

■ 구조

람다 표현식은 기본적으로 아래와 같은 형태를 가진다.

(parameter list) -> {body}
  • parameter list: 타입을 명시할 수도 있고, 타입 추론이 가능한 경우 생략할수도 있다.
    • 파라미터가 하나뿐이라면 괄호 ()를 생략할 수 있다.
  • -> : 람다 표현식의 파라미터와 바디를 구분한다.
  • 바디: 함수의 본문이다.
    • 싱글 스테이트먼트인 경우, 중괄호를 생략할 수 있다.

일반적인 자바 코드와 람다를 사용한 코드를 비교해보자.

// 일반적인 자바 코드
class MyCalculator {
	public int sum(int a, int b){
    	return a+b;
    }
}

위의 코드를 람다식을 사용하도록 고치면 다음과 같이 표현 가능하다.

// 람다 표현식을 사용한 코드
(int a, int b) -> a+b;

■ 함수를 인자로 전달

람다는 함수를 일급 시민으로 만들어 준다.
즉, 함수를 다른 함수의 인자로 전달하고나, 결과 값으로 반환하거나, 데이터 구조에 저장할 수 있다.


다음 코드를 보면, 람다 표현식으로 작성된 익명 함수(여기서는 함수라 칭하겠다)를 forEach() 메소드의 인자로 전달한다.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(n -> System.out.println(n));

어떻게 위와 같은 구조가 가능할까?

처음 위의 식을 봤을때는 전혀 이해가 되지 않았다.
필자의 의문점은 바로 n 이었다.
변수 n을 넘겨줘야 함수가 실행될텐데, 누가 넘겨주는 것일까?

답은 forEach() 메소드에 있었다.
forEach() 메소드는 Consumer 타입의 함수형 인터페이스를 인수로 받는다.
람다식 n -> System.out.println(n)Consumer 타입의 익명 구현 객체인 것이다.

forEach() 메소드는 컬렉션의 각 요소를 익명 구현 객체의 매개변수로 전달한다.




✏️ 스트림

스트림 API는 데이터 컬렉션을 추상화한 것이다.

List, Set, Map 등의 컬렉션을 다루기 위해서는 각각 다른 메소드를 사용해야 한다.
이러한 문제점을 해결하기 위해 도입된 것이 스트림이다.

스트림을 사용하면, 데이터 컬렉션을 추상화하여 공통된 메소드를 적용시킬 수 있다.


스트림은 3가지 과정을 통해 사용할 수 있다.

  1. 스트림 인스턴스 생성
  2. 중간 연산
  3. 최종 연산

■ 스트림의 생성

  1. 배열 스트림
String[] arr = new String[]{"a","b","c"};
Stream<String> stream = Arrays.stream(arr);

  1. 컬렉션 스트림
List<String> list = Arrays.asList("a","b","c");
Stream<String> stream = list.stream();

// 컬렉션은 stream() 메소드를 가지고 있다.

  1. 기본형 스트림
IntStream intStream = IntStream.range(1, 5);

// 제너릭을 사용하지 않기 때문에, 박싱을 하지 않아도 된다.

이외에도 다양한 생성 방식이 존재한다.


■ 중간 연산

생성된 스트림 인스턴스에 대해서 0~n회 중간 연산을 적용할 수 있다.

  • 중간 연산은 스트림을 반환한다.
  • 중간 연산은 최종 연산이 호출되기 전까지는 실행되지 않는다. (Lazy)
  • 여러개의 중간 연산을 이어 붙여서 작성할 수 있다.

중간연산은 다양한 종류가 존재한다.
그 중에서 filter() 메소드와 map() 메소드에 대해서 알아보자.


filter

필터는 스트림 내의 요소들을 하나씩 평가해서 걸러낸다.

  • Predicate 인터페이스를 인자로 받는다.
    Stream<T> filter(Predicate<? super T> predicate)
  • Predicate 인터페이스를 구현한 익명 객체를 전달하는 것이 일반적이다.


    예시:
List<String> names = Arrays.asList("Eric", "Elena", "ju");
Stream<String> stream = names.stream()
  						.filter(name -> name.contains("a"));

map

맵은 스트림 내의 각 요소에 함수를 적용한다.

  • Fucntion 인터페이스를 인자로 받는다.
    Stream<R> map(Function<? super T, ? extends R> mapper);

    예시:
List<String> names = Arrays.asList("Eric", "Elena", "ju");
Stream<String> stream = names.stream()
						.map(name -> name.toUpperCase);

여러개의 중간 연산을 이어 붙일 수 있으므로, 다음과 같이 작성 가능하다.

List<String> names = Arrays.asList("Eric", "Elena", "ju");
Stream<String> stream = names.stream()
  						.filter(name -> name.contains("a"))
                        .map(name -> name.toUpperCase);

■ 최종 연산

최종 연산은 스트림 파이프라인을 실행하고 결과를 도출한다.

  • 생성된 스트림 인스턴스에 대해서 0~1회 최종 연산을 적용할 수 있다.
  • forEach, collect, reduce, sum, max 등이 존재한다.

위의 예제에 collect 연산을 적용하여, 다시 List로 변환하여보자.

List<String> names = Arrays.asList("John", "Michael", "ju");
Stream<String> stream = names.stream()
  						.filter(name -> name.contains("a"))
                        .map(name -> name.toUpperCase)
                        .collect(Collector.toList());



REFERENCE

점프 투 자바 - 박은용

profile
개발자 지망생

0개의 댓글

관련 채용 정보