함수형 프로그래밍 그리고, 람다와 스트림

김성수·2023년 6월 13일
0

Java

목록 보기
13/18

들어가면서

함수형 프로그래밍이 무엇인지, 람다와 스트림이 어떻게 사용되는지에 대해서 정리해본다.


함수형 프로그래밍

Java에서 함수(메서드)를 일급 객체로 취급하여 사용하는 것을 의미한다.

일급 함수 : 객체, 매개변수, return 값으로 사용할 수 있는 함수를 의미한다.

변수나 객체는 일급 함수이지만, 메서드는 이급 함수이다.

왜냐하면 기존의 메서드는 객체로써, 매개변수로써, return 값으로써 사용할 수 없었기 때문이다.

따라서, 매개변수를 객체, 매개변수, return 값으로 사용하기 위해 함수형 프로그래밍을 사용한다.

함수형 프로그래밍은 상태 변경을 피하고 순수 함수를 사용하여 프로그램을 작성하는 것이 권장된다.

순수함수는 입력 값(입력된 매개변수 값)에만 의존하고 외부 상태에 영향을 주지 않는 함수를 의미한다.


순수 함수

아래는 Java 순수 함수 예시이다.

import java.util.ArrayList;
import java.util.List;

public class PureFunctionExample {

    public static int add(int a, int b) {
        return a + b;
    }

    public static List<Integer> doubleList(List<Integer> numbers) {
        List<Integer> doubledNumbers = new ArrayList<>();
        for (int number : numbers) {
            doubledNumbers.add(number * 2);
        }
        return doubledNumbers;
    }

    public static void main(String[] args) {
        int result = add(3, 4);
        System.out.println("Result: " + result); // Output: Result: 7

        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        List<Integer> doubledNumbers = doubleList(numbers);
        System.out.println("Doubled Numbers: " + doubledNumbers); // Output: Doubled Numbers: [2, 4, 6]
    }
}

add 메서드와 doubleList 메서드는 모두 순수함수이다. 외부 상태를 변경하지 않는다.

add 메서드는 입력값 a와 b를 더하여 return 하고

doubleList 메서드는 numbers 리스트의 모든 인자를 2배 곱한 값을 리턴한다.

이러한 순수함수의 장점은 테스트가 용이하고, 병렬 처리와 최적화를 적용할 수 있다.


람다

람다의 기본 문법은 아래와 같다.

(매개 변수) -> {로직};

하지만 로직이 바로 return 하는 경우 더 간결하게 코드를 작성할 수 있다.

(매개 변수) -> 리턴 로직;

아래는 예시 코드


parkingLot.addAll(parkCars(carsWantToPark, Car::hasTicket)); 
parkingLot.addAll(parkCars(carsWantToPark, Car::noTicketButMoney)); 
parkingLot.addAll(parkCars(carsWantToPark, (Car car) -> car.hasTicket() && car.noTicketButHaveMoney() > 1000));


public static List<Car> parkCars(List<Car> carsWantToPark, Predicate<Car> function){
	List<Car> cars = new ArrayList<>();
	
	for(Car car : carsWantToPrak){
		if(function.test(car)){ // 검증 로직
			cars.add(car);
		}
	}

	return cars;
}

public static boolean hasTicket(Car car) {
	return car.hasParkingTicket;
}

public static boolean noTicketButMoney(Car car) {
	return !car.hasParkingTicket && car.getParkingMoney() > 1000;
}

interface Predicate<T> { // 인터페이스로 메서드를 매개변수로 사용할 수 있게함.
	boolean test(T t);
}

위 코드는 주차 가능 차량을 티켓 소유 또는 1000원 이상 금액을 가지고 있는 차량으로 조건을 둔 코드이다.

위 코드의 핵심은 아래와 같다.

  1. 인터페이스로 메서드 내부에서 사용되는 공통 처리를 갈아끼운다.

  2. 인터페이스를 매개변수로 넣어줘서 매개변수에 입력된 메서드를 이용하여 로직을 처리한다.

  3. 매개변수로 메서드를 넘겨주는 문법은 Car::hasTicket과 같다. 타입이 Car가 될 것이고, hasTicket은 메서드명이
    될 것이다.여기서 타입 Car는 인터페이스 타입과 일치해야 한다. pakrCars에서 Predicate<Car>로 선언되어있듯이 말이다.


## 스트림

자료구조의 흐름을 객체로 제공해준다. 그 흐름동안 사용할 수 있는 메서드들을 api로 제공해준다.

한번 사용한 스트림은 어디에도 남지 않는다. 즉, 일회용이다.

모든 컬렉션을 상속하는 구현체들은 스트림을 반환할 수 있다.

// 스트림을 사용하지 않았을 때

ArrayList<Car> benzParkingLotWithoutStream = new ArrayList<>();

for(Car car : carsWaantToPark){
	if(car.getCompany().equals("Benz")){
    	benzParkingLotWithoutStream.add(car);
    }
}


// 스트림을 사용했을 때
ArrayLst<Car> benzParkingLot =
	carsWantToPark.stream()
    	.filter((Car car) -> car.getCompany().equals("Benz"))
        .toList();

fliter와 map 차이

filter -> 조건에 맞는 것만 반환

map -> 모든 요소를 가공해서 반환

// map 예제 코드
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamMapExample {

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 각 숫자를 2배로 변환하여 새로운 리스트 생성
        List<Integer> doubledNumbers = numbers.stream()
                .map(number -> number * 2)
                .collect(Collectors.toList());

        System.out.println("Original Numbers: " + numbers);           // Output: Original Numbers: [1, 2, 3, 4, 5]
        System.out.println("Doubled Numbers: " + doubledNumbers);     // Output: Doubled Numbers: [2, 4, 6, 8, 10]
    }
}

위 코드는 numbers 리스트의 각 요소를 2배로 변환하여 새로운 리스트 doubledNumbers를 생성


forEach()

컬렉션 내부를 돌면서 로직을 처리

// forEach 예제 코드
List<String> carNames = Arrays.asList("Series 6", "A9", "Ionic 6");

carNames.stream()
	.forEach(System.out::println);
    
// 결과
// Series 6
// A9
// Ionic 6
profile
깊이 있는 소프트웨어 개발자가 되고 싶습니다.

0개의 댓글