프로그래밍 공부

JAVA의 함수형 프로그래밍 본문

Programming/JAVA

JAVA의 함수형 프로그래밍

khj1999 2024. 10. 21. 20:43

JAVA의 함수형 프로그래밍이란?

자바에서 함수형 프로그래밍(Functional Programming)은 주로 Java 8에서 도입된 람다 표현식과 스트림 API를 통해 구현된다. 함수형 프로그래밍의 핵심 개념은 다음과 같다.

1. 람다 표현식 (Lambda Expressions)

람다 표현식은 자바에서 익명 함수를 구현하는 방법. 자바 8부터 도입되었으며, 코드의 간결성을 높이고, 불필요한 클래스 정의를 줄이는 데 도움을 준다.

  • 형식: (parameters) -> expression 또는 (parameters) -> { statements; }

  • 예제: 람다 표현식을 사용하여 함수를 구현

      interface MyFunctionalInterface {
          void myMethod(String s);
      }
    
      public class LambdaExample {
          public static void main(String[] args) {
              // 람다 표현식 사용
              MyFunctionalInterface myFunc = (s) -> System.out.println(s);
              myFunc.myMethod("Hello, Lambda!");
          }
      }
    

    위의 코드에서 myFuncmyMethod라는 메서드를 가지며, 람다 표현식을 사용해 구현되었다. 이를 통해 myFuncmyMethod를 호출할 때 주어진 문자열을 출력함.

2. 메서드 참조 (Method References)

메서드 참조는 기존 메서드를 간편하게 참조할 수 있는 방법으로, 람다 표현식의 간단한 형태. 주로 :: 연산자를 사용하여 정의함.

  • 형식: ClassName::methodName 또는 instance::methodName

  • 예제: 위의 인터페이스를 사용하여 String 출력

      class MyClass {
          public static void print(String s) {
              System.out.println(s);
          }
      }
    
      public class MethodReferenceExample {
          public static void main(String[] args) {
              // 메서드 참조 사용
              MyFunctionalInterface myFunc = MyClass::print;
              myFunc.myMethod("Hello, Method Reference!");
          }
      }
    

    이 예제에서 MyClass::printprint 메서드를 참조하고 있으며, myFunc를 호출하면 해당 메서드가 실행됩니다.

3. 스트림 (Streams)

스트림은 자바 8부터 도입된 데이터 처리의 추상화로, 대규모 데이터 집합을 간편하게 처리할 수 있게 해준다. 스트림 API는 다양한 연산을 제공하며, 람다 표현식을 통해 데이터를 변환하고 필터링할 수 있다.

  • 주요 메서드:

    • filter(): 조건에 맞는 요소를 필터링
    • map(): 요소를 변환
    • reduce(): 집계 연산
    • forEach(): 각 요소에 대해 동작 수행
  • 예제: 스트림을 사용해 ‘A’ 로 시작하는 이름 필터링

      import java.util.Arrays;
      import java.util.List;
    
      public class StreamExample {
          public static void main(String[] args) {
              List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    
              // 스트림을 사용하여 이름 필터링 및 출력
              names.stream()
                   .filter(name -> name.startsWith("A")) // "A"로 시작하는 이름 필터링
                   .forEach(System.out::println); // 결과 출력: Alice
          }
      }
    

4. 고차 함수 (Higher-Order Functions)

고차 함수는 다른 함수를 매개변수로 받거나, 함수를 반환하는 함수를 의미한다. 자바에서는 주로 함수형 인터페이스를 사용하여 이러한 기능을 구현한다. 이는 코드의 재사용성을 높이고, 기능을 조합하는 데 유용하다.

  • 예제: 이 예제에서 applyFunction은 함수를 매개변수로 받아 해당 함수를 실행.

      import java.util.function.Function;
    
      public class HigherOrderFunctionExample {
          public static void main(String[] args) {
              Function<Integer, Integer> square = x -> x * x; // 제곱하는 함수
              System.out.println("Square of 5: " + applyFunction(5, square)); // 25 출력
          }
    
          public static Integer applyFunction(Integer value, Function<Integer, Integer> function) {
              return function.apply(value); // 함수 적용
          }
      }
    

5. 불변성 (Immutability)

함수형 프로그래밍에서는 상태 변경을 피하고 불변 객체를 사용하는 것을 선호한다. 불변 객체는 생성 후 상태가 변경되지 않으므로, 프로그램의 예측 가능성이 높아지고, 동시성 문제를 줄일 수 있다.

  • 예제1: 위의 ImmutablePoint 클래스는 불변성을 가지며, 생성자에서 초기화한 이후에는 상태를 변경할 수 없다.

      final class ImmutablePoint {
          private final int x;
          private final int y;
    
          public ImmutablePoint(int x, int y) {
              this.x = x;
              this.y = y;
          }
    
          public int getX() {
              return x;
          }
    
          public int getY() {
              return y;
          }
      }
    
      public class ImmutableExample {
          public static void main(String[] args) {
              ImmutablePoint point = new ImmutablePoint(5, 10);
              System.out.println("Point: (" + point.getX() + ", " + point.getY() + ")");
              // point.x = 20; // 에러 발생! 불변 객체이므로 상태 변경 불가
          }
      }
    
  • 예제2: 1부터 10까지의 값을 더하는 함수형 프로그래밍 방식과 기존의 방식

    
      import java.util.stream.IntStream;
    
      // 함수형 방식
      public class FunctionalSumStream {
          public static void main(String[] args) {
              int sum = IntStream.rangeClosed(1, 10)  // 1부터 10까지의 정수 스트림 생성
                                 .sum();  // 스트림의 합을 구함
              System.out.println("Sum (functional stream): " + sum);  // 55 출력
          }
      }
    
      // 기존 방식
      public class ImperativeSum {
          public static void main(String[] args) {
              int sum = 0;  // 상태 변경 가능한 변수
              for (int i = 1; i <= 10; i++) {
                  sum += i;  // 매 반복마다 상태(변수 sum)가 변함
              }
              System.out.println("Sum (imperative): " + sum);  // 55 출력
          }
      }

정리

이러한 개념들은 자바에서 함수형 프로그래밍을 효과적으로 구현하는 데 도움을 주며, 코드의 가독성과 유지보수성을 높이는 데 기여할 수 있다.
함수형 프로그래밍의 원칙을 따르며 개발하면, 더 간결하고 오류가 적은 코드를 작성할 수 있는것 같다.

'Programming > JAVA' 카테고리의 다른 글

자바 스레드에서 Runnable 인터페이스를 사용하는 이유  (0) 2024.10.22
자바 스레드(Thread)  (1) 2024.10.22
자바 제네릭  (0) 2024.10.09
자바 컬렉션 - 객체 정렬  (0) 2024.10.09
자바 컬렉션 - Map  (0) 2024.10.08
Comments