Java

2024.09.13 Java 유용한 클래스 - 3 람다식(Lambda expression)

정훈5 2024. 9. 13. 09:19

 

학습 목표 

1. 람다식의 기본 개념과 문법을 직접 말할 수 있다. 
2. 기존에 자바 코드를 람다식 표현법을 사용해서 작업할 수 있다. 
3. 함수형 인터페이스(Functional Interface)가 무엇인지 간단하기 말할 수 있다.
4. 자바8 버전부터 제공해주는 interface Function<T, R> 을 활용할 수 있다.

 

자바에서 람다식(Lambda expression)은 함수형 프로그래밍 개념을 도입한 것으로, 익명 함수(이름이 없는 함수)를 간결하게 표현할 수 있게 해 줍니다, 람다식은 자바 버전 8(2014년 출시)부터 사용할 수 있습니다. 추가로 자바 8은 함수형 프로그래밍을 지원하기 위한 여러 기능을 함께 도입했습니다

즉, 람다식을 사용하면 코드가 간결해지고, 특히 컬렉션이나 스트림 API 을 처리할 때 매우 유용합니다.

람다식은 주로 간단한 작업을 한 줄의 코드로 처리할 때 많이 사용하며 예를 들어, 리스트의 요소를 하나씩 처리할 때 기존 방식보다 훨씬 간단하게 표현할 수 있습니다.(스트림 API 학습 후 알아 봅시다)

 

 

함수형 프로그래밍 (Functional Programming)

수학적 함수의 개념에 기반한 함수를 기본 단위로 사용하여 문제를 해결하는 프로그래밍 패러다임입니다.

  1. 순수 함수
    입력이 동일하면 항상 동일한 출력을 반환하는 함수로, 함수의 실행이 프로그램 상태나 외부 변수에 의존하지 않습니다.
    즉, 부작용(side effects)이 없습니다.

  2. 불변성
    함수형 프로그래밍에서는 데이터를 변경하지 않고, 변경이 필요할 때는 기존 데이터를 복사한 후 수정합니다.
    이는 불변 객체와 연결됩니다.

  3. 고차 함수
    함수인자로 전달하거나 함수에서 함수를 반환할 수 있습니다.
    함수는 일급 객체(First-class citizen)로 취급됩니다.

  4. 함수 조합
    여러 함수를 조합하여 더 복잡한 함수를 만들 수 있으며, 이를 통해 코드를 간결하고 모듈화할 수 있습니다.

  5. 대표 언어
    Haskell, Scala 등

 

lamda.ex

 

시나리오 코드1

package ch01;

// 람다식을 사용하기 위해서는 함수형 인터페이스가 먼저 만들어 져 있어야 한다.
@FunctionalInterface
interface MathOperation {
	int operate(int x, int y);
	// 람다 인터페이스 안에는 2개 이상 만들 수 없다.
}

public class MainTest {
	
	
	public static void main(String[] args) {
		
		// 핵심 !
		// 함수형 인터페이스를 활용해서 내가 필요한 식을(람다 형태)를 직접 정의한다. 
		// 식을 직접 만들고 필요한 시점에 간결하게 사용한다. 
		
		// 식을 만들어 보자. (장점 : 간결하게 사용할 수 있다. )
		MathOperation add = (int x, int y) -> {return x + y;};
		MathOperation subtract = (int x, int y) -> {return x - y;};
		MathOperation divide = (int x, int y) -> {return x / y;};
		MathOperation multiply = (int x, int y) -> {return x * y;};
		
		// 람다식을 호출하여 결과를 출력해 보자.
		System.out.println("10 + 10 = " + add.operate(10, 10));
		System.out.println("10 - 10 = " + subtract.operate(10, 10));
		System.out.println("10 / 10 = " + divide.operate(10, 10));
		System.out.println("10 * 10 = " + multiply.operate(10, 10));
		
	}

}

 

시나리오 코드

package ch01;

// 람다식을 사용하기 위해서는 
// 함수형 인터페이스가 먼저 만들어져 있어야 한다. 
@FunctionalInterface
interface MathOperation {
	int operate(int x, int y);
	//int perate1(int x, int y);
}


public class MainTest {
	public static void main(String[] args) {
		
		// 핵심 ! 
		// 함수형 인터페이스를 활용해서 내가 필요한 식을(람다 형태)을 직접 정의한다.
		// 식을 만들었다면 필요한 시점에 간결하게 호출해서 사용한다. 
		
		// 식을 만들어 보자. 
		MathOperation add =  (int x, int y) ->  x + y;
		MathOperation subtract =  (int x, int y) ->  x - y;
		MathOperation divide =  (int x, int y) ->  {
			if(y == 0) {
				System.out.println(" Error : 어떤 수를 0으로 나눌 수 없음!! ");
				return 0; 
			} else {
				return x / y;
			} 
		};
		MathOperation multiply =  (int x, int y) ->  x * y;
		
		
		// 람다식을 호출하여 결과를 출력해 보자. 
		System.out.println("10 + 10 = " + add.operate(10, 10));
		System.out.println("10 - 10 = " + subtract.operate(10, 10));
		System.out.println("10 / 10 = " + divide.operate(10, 10));
		System.out.println("10 * 10 = " + multiply.operate(10, 10));
		
		
	}

}

 

 

시나리오 코드2

// 객체 지향적으로 계산기를 구현
class Calculator {

    public int add(int x, int y) {
        return x + y;
    }

    public int subtract(int x, int y) {
        return x - y;
    }

    public int multiply(int x, int y) {
        return x * y;
    }

    public int divide(int x, int y) {
        if (y == 0) {
            throw new IllegalArgumentException("Cannot divide by zero");
        }
        return x / y;
    }
}

public class MainTestOOP {
    public static void main(String[] args) {
        // 객체를 생성해서 메서드를 호출
        Calculator calculator = new Calculator();

        System.out.println("10 + 5 = " + calculator.add(10, 5));
        System.out.println("10 - 5 = " + calculator.subtract(10, 5));
        System.out.println("10 * 5 = " + calculator.multiply(10, 5));
        System.out.println("10 / 5 = " + calculator.divide(10, 5));
    }
}

 

 

차이점 확인 !!

객체 지향 프로그래밍에서는 객체를 중심으로 코드를 작성하고, 그 객체 내의 메서드를 통해 동작을 정의합니다.

반면에 람다식은 함수형 프로그래밍 스타일을 반영하여 함수 자체를 간결하게 정의하고 사용합니다.

 

 

람다식(함수형 프로그래밍) 방식 예제 살펴 보기

// 함수형 인터페이스 선언
@FunctionalInterface
interface MathOperation {
    int operate(int x, int y);
}

public class MainTestFP {

    public static void main(String[] args) {

        // 각 연산을 람다식으로 정의
        MathOperation add = (x, y) -> x + y;
        MathOperation subtract = (x, y) -> x - y;
        MathOperation multiply = (x, y) -> x * y;
        MathOperation divide = (x, y) -> {
            if (y == 0) {
                throw new IllegalArgumentException("Cannot divide by zero");
            }
            return x / y;
        };

        // 람다식으로 정의된 연산을 호출
        System.out.println("10 + 5 = " + add.operate(10, 5));
        System.out.println("10 - 5 = " + subtract.operate(10, 5));
        System.out.println("10 * 5 = " + multiply.operate(10, 5));
        System.out.println("10 / 5 = " + divide.operate(10, 5));
    }
}

 

 

 

 

  • 객체 지향 프로그래밍은 상태와 동작을 함께 관리하고 구조적으로 코드를 작성하는 데 강점이 있습니다.
  • 람다식함수형 프로그래밍은 간단한 동작을 간결하게 표현하고, 함수 자체를 유연하게 재사용할 수 있습니다.

즉, 각 상황에 따라 장점이 다르므로, 어떤 작업을 수행하느냐에 따라 적합한 방식을 선택하는 것이 중요합니다.

 


Function 인터페이스에 대해 알아 보자.

Function<T, R> 인터페이스는 자바에서 한 개의 입력을 받아서 하나의 출력을 반환하는 함수형 인터페이스입니다.

T는 입력 타입, R은 반환 타입을 의미합니다.

 

개발 목적 자바 8에서 함수형 프로그래밍의 개념이 도입되면서, 함수를 일급 객체로 취급할 수 있는 방식이 필요했기 때문에 제공되는 인터페이스 입니다(자바 개발자들이 만들어 준 인터페이스 ! )

 

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

 

예시

  • Function<Integer, Double>: 입력은 Integer 타입(정수)이고, 출력은 Double 타입(실수)입니다.
  • Function<String, Integer>: 입력은 String 타입(문자열)이고, 출력은 Integer 타입(정수)입니다.

이 함수형 인터페이스는 주로 데이터를 변환하거나 처리할 때 유용합니다.

예를 들어, 특정 값을 받아서 그 값을 가공한 결과를 반환하는 로직을 간단하게 람다식으로 작성할 수 있습니다.

시나리오 코드 3

문제 해결 - 과일 상점에 상품들을 할인된 가격으로 출력 시켜야 한다.

 

과일 상점에서 세 가지 과일을 판매합니다:

  • 사과 1개의 가격은 1200원
  • 바나나 1개의 가격은 500원
  • 오렌지 1개의 가격은 800원

각 과일의 수량을 입력받고(x 값이 되고) , 각 과일의 구매 총액을 계산한 후 10% 할인된 가격을 계산하는 프로그램을 작성하세요.

 

package ch01;

import java.util.function.Function;

public class FruitShop {

	public static void main(String[] args) {
		// 함수형 인터페이스 ---> Function<T, R>

		// 1. 각 과일의 가격을 람다식으로 만들어보자. 
		// 예 : 사과 1 개는 가격 1200원 이다. 
		Function<Integer, Integer> applePrice = x -> x * 1200;
		Function<Integer, Integer> bananaPrice = x -> x * 500;
		Function<Integer, Integer> orangePrice = x -> x * 800;
		
		
		// 2. 사용 부분 - 각 과일을 10개씩 구매했을 때 가격을 계산하세요 
		System.out.println("사과 10개의 가격은 : " + applePrice.apply(10));
		System.out.println("바나나 10개의 가격은 : " + bananaPrice.apply(10));
		System.out.println("오렌지 10개의 가격은 : " + orangePrice.apply(10));
		
		// 3. 10% 할인을 적용하는 람다식을 작성하세요. -> 0.9 
		Function<Integer, Integer> discount = price -> (int)(price * 0.9); // 10% 할인
		
		// 4. 할인된 금액을 출력해 보자.
		System.out.println("사과 10개의 할인된 가격 : " + discount.apply(applePrice.apply(10)));
		System.out.println("바나나 10개의 할인된 가격 : " + discount.apply(bananaPrice.apply(10)));
		System.out.println("오렌지 10개의 할인된 가격 : " + discount.apply(orangePrice.apply(10)));
		
		
	}
}

 

 

도전과제 

두 수를 입력 받아 큰 값을 출력하는 기능을 만들어 보세요 단, 람다 표현식을 사용하세요 

1. 함수형 인터페이스 선언 
2. 람다 표현식 설계 
3. 데이터 입력후 결과 확인

 

package ch01;

import java.util.Scanner;

// 1. 함수형 인터페이스 선언

@FunctionalInterface
interface MaxOperation {
	int max(int a, int b);
}


public class MaxValueFinder { 
	
	public static void main(String[] args) {
		
		// 2. 람다식 설계 : 두 수를 비교하여 큰 값을 반환하는 식을 작성
		// 아래에 문제를 푸세요
		MaxOperation finMax = null;
		
		// 3. 데이터 입력
		Scanner scanner = new Scanner(System.in);
		
		System.out.println("첫번째 숫자를 입력하세요. ");
		int num1 = scanner.nextInt();
		
		System.out.println("두번째 숫자를 입력하세요. ");
		int num2 = scanner.nextInt();
		
		// 4. 람다식 호출 및 결과 출력
		
	}
}

 

풀이

package ch01;

import java.util.Scanner;

// 1. 함수형 인터페이스 선언

@FunctionalInterface
interface MaxOperation {
	int max(int a, int b);
}


public class MaxValueFinder { 
	
	public static void main(String[] args) {
		
		// 2. 람다식 설계 : 두 수를 비교하여 큰 값을 반환하는 식을 작성
		// 아래에 문제를 푸세요
	//	MaxOperation finMax = null;
	//	MaxOperation finMax = () -> {};
		MaxOperation findMax = (a, b) -> a > b ? a : b;
		
		// 3. 데이터 입력
		Scanner scanner = new Scanner(System.in);
		
		System.out.println("첫번째 숫자를 입력하세요. ");
		int num1 = scanner.nextInt();
		
		System.out.println("두번째 숫자를 입력하세요. ");
		int num2 = scanner.nextInt();
		
		// 4. 람다식 호출 및 결과 출력
		int result = findMax.max(num1, num2);
		System.out.println("두 수중 더 큰 값은 : " + result + " 입니다.");
		
	}
}