Study/Java

[Java] 람다, 스트림, Optional

욘아리 2024. 4. 22. 16:56

자바는 계속 진화하며 가장 큰 진화는 Java 8에서 이루어졌다. 그렇기 때문에 자바에는 여러 버전이 있지만 최소한 Java 8 버전 이상의 문법에 익숙해질 필요가 있다.

 

오늘은 Java 8 버전의 주요 문법에 대해 정리해보려고 한다. 

주요 문법에는 람다 표현식, 스트림 API, Optional이 있다.


람다 표현식

람다 표현식(Lambda Expression)은 익명 함수를 간략하게 표현하는 방법이다.

 

자바 기본 문법에서는 함수만 따로 선언할 수 없고 클래스가 있어야 한다. 임시로 사용할 함수 때문에 매번 클래스를 선언하고 메서드를 선언하는 것은 불편하고, 익명 클래스를 사용하더라도 불필요한 코드가 많이 추가되어 코드의 가독성도 떨어진다.

이럴 땐 람다식을 이용하여 메서드를 하나의 식으로 간략하고 명확한 식으로 표현하면 된다.

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

// 익명 클래스 사용
numbers.forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer number) {
        System.out.println(number);
    }
});

// 람다 표현식 사용
numbers.forEach(number -> System.out.println(number));
  • 동일한 기능을 하는 코드가 람다 표현식으로 바뀌었을 때 간결해지고 가독성이 향상된 것을 확인할 수 있다.

 

스트림 API

스트림 API는 컬렉션에 추가된 메서드의 집합을 의미하고, 스트림은 컬렉션에 담겨 있는 데이터를 마치 물이 흐르는 것처럼 처리한다는 데서 유래한 이름이다. 스트림 API를 통해 데이터를 손쉽게 필터링, 매핑, 정렬 및 집계할 수 있다.

스트림 API는 스트림 API가 호출되는 컬렉션 객체의 요소들을 인자로 들어온 람다 표현식을 통해 처리한다.

 

스트림 API 중 대표적으로 자주 사용되는 메서드 예시들만 살펴보자.

 

forEach()

Integer[] integerArray = new Integer[]{1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(integerArray);
list.stream().forEach(value -> System.out.println(value));

// 결과
// 1 2 3 4 5
  • 컬렉션의 요소들을 하나씩 꺼내서 반복하므로 반복문을 쉽게 대처할 수 있다.

map()

String[] lowercaseArray = new String[]{"public", "static", "void"};
        List<String> lowercaseList = Arrays.asList(lowercaseArray);
        List<String> uppercaseList = lowercaseList.stream()
                .map(value -> value.toUpperCase()).toList();
        uppercaseList.stream().forEach(value -> System.out.println(value));
        
// 결과
// PUBLIC STATIC VOID
  • 컬렉션의 요소들을 특정 연산을 적용한 새로운 스트림을 만든다.

filter()

Integer[] integerArray = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<Integer> list = Arrays.asList(integerArray);
List<Integer> evenList = list.stream()
        .filter(value -> value % 2 == 0).collect(Collectors.toList());
evenList.stream().forEach(value -> System.out.println(value));

// 결과
// 2 4 6 8 10
  • 컬렉션의 요소들 중 조건문에 맞는 요소만 뽑아 새로운 스트림을 만든다.

distinct()

Integer[] integerArray = new Integer[]{1, 1, 1, 1, 2, 2, 2, 3, 3, 4};
List<Integer> list = Arrays.asList(integerArray);
List<Integer> distinctList = list.stream().distinct().toList();
distinctList.stream().forEach(value -> System.out.println(value));

// 결과
// 1 2 3 4
  • 컬렉션의 요소에서 중복을 제거한다. 중복을 제거한다는 행위가 이미 정해져 있기 때문에 람다 표현식을 함수의 인자로 넘겨줄 필요가 없고, 이때 중복인지 아닌지를 판단하는 것은 요소들의 equals() 메서드이다.

 

Optional

Optional은 Null이 들어 있는 레퍼런스 변수의 멤버(필드나 메서드)에 접근하려고 할 때 발생하는 예외인 NullPointerException을 해결하기 위해 등장했다.

 

Optional을 사용하면 if 문으로 null을 체크하는 코드를 아래와 같이 개선할 수 있다.

private static Optional<String> getSomeString() {
// null을 반환하는 것이 아니라 비어 있는 Optional을 반환한다.
    return Optional.empty(); 
}

public static void main(String[] args) {
    Optional<String> isThisNull = getSomeString();

    isThisNull.ifPresent(str -> System.out.println(str.toUpperCase()));
}
  • 메서드에서 반환된 문자열이 비어 있지 않은 경우에는 ifPresent의 인자로 들어간 람다 표현식을 실행하고, 비어 있는 경우에는 실행되지 않는다.

비어있는 Optional이 아니라 값을 포함한 Optional을 반환하려면 아래와 같이 작성하면 된다.

private static Optional<String> getSomeString() {
    return Optional.ofNullable("public static void");
}

public static void main(String[] args) {
    Optional<String> isThisNull = getSomeString();

    isThisNull.ifPresent(str -> System.out.println(str.toUpperCase()));
}

// 결과
// PUBLIC STATIC VOID

 

 

✨👩‍💻 ✨

자바 8에서의 람다, 스트림, 옵셔널은 실제 자바 프로그래밍의 패러다임을 바꾸는 중요한 변화 중 하나였다. 이전의 자바에서는 많은 번거로움과 예외 처리가 필요했으나, 이러한 기능들이 도입되면서 코드를 더 간결하고 편리하게 만들어 주었다.

 

람다 표현식으로 익명 클래스를 사용한 장황한 코드를 간결하게 개선하고, 스트림 API를 통해 조건문과 반복문으로 이루어진 코드를 간결하게 개선하고, Optional을 통해 자바에서 null을 안전하고 효과적으로 다룰 수 있다.

 

처음부터 당연하게 사용해 왔던 것들을 이번 기회에 정리해 볼 수 있어서 좋았고, 앞으로도 코드의 가독성과 최적화를 위해 알맞게 사용해야겠다.

 

 

출처

이준형, 이것이 취업을 위한 백엔드 개발이다 with 자바