본문 바로가기
Java/계산기 구현

3단계 계산기 - Lambda 및 Stream을 활용한 인터페이스 구현

by JuNo_12 2025. 4. 22.

우선 전체 코드를 보면서 한 줄 한 줄 보자.

아래는 람다, 스트림을 활용하여 저장된 연산 결과들 중 Scanner로 입력받은 값보다 큰 결과값들을 출력하는 인터페이스를 작성한 코드이다.

package calculator;

import java.util.List;
import java.util.Scanner;

@FunctionalInterface

public interface ResultFilter {

        void filter(List<Double> results);

        ResultFilter resultFilter = (results) -> {
        Scanner sc = new Scanner(System.in);
        System.out.print("기준값을 입력하세요: ");
        double filterNumber = sc.nextDouble();

        List<Double> filtered = results.stream() // 스트림 활용
                .filter(num -> num > filterNumber)
                .toList();

        if (filtered.isEmpty()) {
            System.out.println("입력한 값보다 큰 결과가 없습니다.");
        } else {
            System.out.print("결과값들 : ");
            filtered.forEach(num -> System.out.print(num + "  /  "));
            System.out.println();
        }
    };
}

 

 

전체 구조를 간단하게 정리하면,

@FunctionalInterface
public interface ResultFilter {
    void filter(List<Double> results);

    ResultFilter resultFilter = (results) -> {
        ...
    };
}

 

ResultFilter 는 filter 라는 추상 메서드 하나만 가지므로 '@FunctionalInterface'로 선언해주었다. 아래에서 자세하게 다뤄보자. (가장 핵심인 부분이기때문이다.)

void filter(List<Double> results);

 

이 부분이 가장 중요한데, 이 한 줄이 사실 이 인터페이스의 핵심 메서드 정의이다.

이건 ResultFilter라는 함수형 인터페이스의 추상 메서드 선언부이다.

- void

  • 이 메서드는 반환값이 없다는 뜻이다.
  • 즉, 뭔가 작업을 하지만 결과를 리턴하지 않고, 주로 출력하거나 내부에서 다른 처리를 한다.

- filter

  • 이건 메서드 이름이다.
  • 외부에서 이 인터페이스를 사용할 때는 filter()라는 이름으로 호출하게 된다.
  • 이름 그대로, 주어진 리스트에서 특정 기준에 따라 필터링 작업을 수행할 것으로 예상할 수 있다.

- (List<Double> results)

  • 이건 매개변수이다.
  • results라는 이름의 Double형 리스트를 인자로 받는다.

정리하자면, resultFiler는 이 인터페이스를 구현하는 람다식으로 정의된 인스턴스이다.

 

자바의 @FunctionalInterface추상 메서드가 딱 하나만 있는 인터페이스이다.

그래서 이 filter 메서드는 ResultFilter 인터페이스가 람다식으로 사용할 수 있게 해주는 핵심 규약이다.

 

(results) -> {
    // 무언가 실행
}

 

이 filter(List<Double> results) 메서드의 구현체 역할을 하는 것이다.

 

이제 내부의 람다식을 보자.

ResultFilter resultFilter = (results) -> {
        Scanner sc = new Scanner(System.in);
        System.out.print("기준값을 입력하세요: ");
        double filterNumber = sc.nextDouble();

        List<Double> filtered = results.stream() // 스트림 활용
                .filter(num -> num > filterNumber)
                .toList();

        if (filtered.isEmpty()) {
            System.out.println("입력한 값보다 큰 결과가 없습니다.");
        } else {
            System.out.print("결과값들 : ");
            filtered.forEach(num -> System.out.print(num + "  /  "));
            System.out.println();
        }
    };

 

사실 볼 부분은 아래 부분인데

        List<Double> filtered = results.stream() // 스트림 활용
                .filter(num -> num > filterNumber)
                .toList();

-  .filter(num -> num > filterNumber)

  • .filter(...)는 스트림에서 조건에 맞는 요소만 남기는 중간 연산.
  • num -> num > filterNumber는 람다 표현식: 리스트의 각 요소 num이 기준값보다 클 때만 필터링.
  • 즉, 조건을 만족하는 값들만 스트림에 남긴다.

-  .toList();

  • 필터링된 결과를 리스트로 다시 수집 
  • 최종적으로 filtered는 기준값보다 큰 값들만 포함한 리스트가 된다.

자바 스트림에서 자주 사용하는 람다식을 표로 정리해두었으니 참고해보자.

메서드 람다식 예시 설명
.filter() list.stream().filter(x -> x > 10) 조건을 만족하는 요소만 필터링 (여기선 10 초과)
.map() list.stream().map(x -> x * 2) 요소를 변환 (2배로 변환)
.forEach() list.stream().forEach(x -> System.out.println(x)) 각 요소에 대해 작업 수행 (출력 등)
.sorted() list.stream().sorted((a, b) -> a - b) 정렬 기준 지정 (오름차순 정렬)
.distinct() list.stream().distinct() 중복 제거
.limit() list.stream().limit(5) 앞에서 5개 요소만 추출
.skip() list.stream().skip(3) 앞에서 3개 요소 건너뜀
.reduce() list.stream().reduce(0, (a, b) -> a + b) 누적 연산 (합계 계산 등)
.anyMatch() list.stream().anyMatch(x -> x > 50) 하나라도 조건 만족하면 true
.allMatch() list.stream().allMatch(x -> x > 0) 모두 조건 만족해야 true
.noneMatch() list.stream().noneMatch(x -> x < 0) 모두 조건 불만족해야 true
.collect() list.stream().filter(...).collect(Collectors.toList()) 최종적으로 결과 수집 (리스트 등으로)

 

그리고 또 굉장히 중요한 정보!

또한, 인터페이스는 클래스와는 다르게 안에 static final 필드처럼 정의된 람다 인스턴스를 바로 가지고있어서, 별도의 클래스 인스턴스 생성없이 바로 접근할 수 있다. 만약, 클래스였다면 따로 객체를 호출해주어야하고 추가적으로 public static final ResultFilter resultFilter 이런 식으로 선언해주어야한다. 안그러면 메인 클래스에서 활용못해!

 

메인클래스에서 호출

ResultFilter.resultFilter.filter(resultHistory.getResults());
  • ResultFilter: 인터페이스 이름
  • .resultFilter: 정적으로 선언된 람다식 인스턴스
  • .filter(...): filter 메서드를 호출해서 리스트를 전달