javaboiii의 JAVA/JAVA 요약정리(멘토씨리즈)

JAVA - 17) 람다식

javaboiii 2024. 7. 28. 01:10

JAVA

1. 람다식 (Lambda expression)

자바는 JDK 1.8부터 함수 프로그래밍 '람다식(Lambda expression)'을 지원하고 있습니다. 람다식은 함수의 이름이 없는

익명 함수(anonymous function)를 만들기 위한 표현식을 말합니다.

자바는 객체를 기반으로 프로그램을 구현하는 객체 지향 프로그램입니다. 따라서 클래스를 먼저 생성하고, 클래스 안에 메서드와 객체를 만들어 사용해야 합니다. 하지만, 함수형 프로그래밍은 객체 지향 프로그램과 달리 함수만들 구현하고 실행할 수 있는 개발 방식입니다. 

 

자바에서는 함수, 즉 메서드를 사용하기 위해 많은 과정을 거치게 됩니다. 이러한 과정을 생략하여 함수를 하나의 간결한

식으로 표현한 것이 람다식입니다. 즉 '식별자 없이 실행 가능한 함수'를 말합니다.

함수와 메서드 차이 : 함수는 클래스 독립적이고 메서드는 클래스 종속적입니다.

2. 람다식 문법

람다식 문법은 자바스크립트나 다른 함수형 프로그래밍에서 볼 수 있었던 문법 패턴들로 기존의 자바 문법과는 달라서 객체 지향 프로그래밍에 익숙한 사람은 생소하게 다가올 수 있습니다. 하지만 문법이 매우 간결해지고, 원하는 결과를 쉽게 집계할 수 있어 익숙해지면 큰 장점이 있는 표현식입니다.

 

자바 문법

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

 

 

람다식 문법

(x, y) ->{return x+y;}

 

람다식 기본 문법

(x,y)   ->  {retrun x+y;}   
매개변수	함수 구현

 

메서드의 이름과 반환 타입을 제고하고 화살표 기호(→)를 사용해 구현합니다. 의미를 살펴보면 두 개의 매개변수(x,y)를

사용해 더한 결과를 반환하라는 의미입니다.

소괄호 생략하기

람다식 문법에서는 매개변수 자료형을 생략할 수 있으며, 매개 변수가 한 개인 경우에는 소괄호도 생략할 수 있습니다.

그러나 매개 변수가 두개 이상일 경우에는 생략할 수 없습니다.

(str) ->{System.out.println(str);} //매개변수가 한 개일 때

str -> {System.out.println(str);} //소괄호 생략 가능

중괄호 생략하기

함수의 구현 내용이 한문장이거나 return이 없다면 즉 함수의 return 타입이 void 라면 구현부에 있는 중괄호{ }도 생략할 수 있습니다. 만약 함수의 내용이 두 문장 이상이거나, return 타입이 있는 경우에는 생략할 수 없습니다.

(x,y) -> {System.out.println(x+y);} //return이 없는 경우

(x,y) -> System.out.println(x+y); // 중괄호 생략 가능

return 생략하기

중괄호 안의 구현 코드가 return문만 존재할 경우 중괗로와 return을 모두 생략할 수 있습니다.

(x,y) -> {return x > y ? 1 : 0;} // return문만 있는 경우

(x,y) -> x > y ? 1 : 0; // 중괄호와 return 구문 생략 가능

3. 함수형 인터페이스

객체지향 프로그램에서 인터페이스를 사용하려면 인터페이스 클래스에 상속 시킨 뒤 내용을 구현해야 합니다. 

람다식을 이용해 인터페이스를 사용할 경우, 인터페이스는 하나의 기능만 정의할 수 있습니다. 기존의 자바에서 사용하는 인터페이스는 여러개의 메서드를 정의할 수 있었지만 람다식은 모든 인터페이스를 람다식의 타켓 타입으로 사용할 수 없습니다. 람다식을 구현하기 위해서느 먼저 인터페이스를 만들고, 인터페이스에 람다식으로 구현할 메서드를 선언해야 합니다. 오직 하나의 추상 메서드가 선언된 인터페이스만이 람다식의 타겟 타입이 될 수 있는데 이러한 인터페이스를

'함수형 인터페이스'라고 합니다.

 

람다식은 메서드명이 없는 익명 함수로 구현하기 때문에 인터페이스에 여러 개의 메서드가 있을 경우 구분하기가

모호해집니다. 따라서 함수형 인터페이스를 만들 때는 하나의 메서드만 정의하도록 합니다.

람다식은 대입될 인터페이스의 종류에 따라 작성 방법이 달라지기 때문에 람다식이 대입될 인터페이스를 람다식의 타겟 타입이라고 합니다.

함수형 인터페이스 선언

익명 함수로 구현하기 때문에 하나의 메서드만을 지니도록 선언합니다.

public interface CompareNumber{
	int CompareTo(int num1, int num2);
}

 

간혹 프로그래밍을 하다 보면 람다식으로 구현한 인터페이스에 실수로 두 개 이상의메서드를 추가하는 오류를 범합니다.

이를 방비하고자 어노테이션을 부여해 제한할 수 있는데 이때 사용하는 어노테이션이 @FunctionalInterface 입니다.

@FunctionalInterface
public interface CompareNumber{
	int compareTo(int num1, int num2);
}

 

이와 같이 @FunctionalInterface 어노테이션을 부여하면, 함수형 인터페이스라는 의미이며, 메서드를 두 개 이상 선언하면

문법적으로 오류를 발생시킵니다.

// 인터페이스 객체 선언 시 람다식을 이용해 함수를 구현
CompareNumber compare = 
(num1, num2) -> {return num1>num2 ? 1:num1< num2 ? -1:0;};

람다식과 외부변수의 관계

람다식을 사용할 때 매개변수로 값을 전달하는 것 외에 외부에서 정의됨 지역변수를 사용하는 경우가 있습니다.

이렇게 지역변수를 사용할 때는 유의해야 할 사항이 있습니다. 만약 람다식 내부에서 지역변수를 사용하려면

그 지역변수는 final로 선언되어야 합니다.

 

지역변수를 선언한 후 람다식 안에서 재정의할 경우, 문법 오류가 나타납니다.

지역변수는 stack 메모리 영역에 생성되고, 람다식의 경우 익명 객체를 만들기 때문에 Heap 영역에 생성됩니다.

서로 생성되는 위치가 다르므로 간섭할 수 없습니다. 람다식 내부에서 지역변수를 사용할 경우 복사해 사용하므로

값을 그래도 사용하는 것은 가능하지만 수정은 할 수 없습니다. 이것을 'variable capture'라고도 합니다.

 

JDK 1.8 이전에는 람다식 또는 익명 클래스 안에 지역변수를 사용할 경우, final 키워드를 부여해 변경 불가 변수임을

명시해야 했습니다. 그러나 JDK 1.8 이후부터는 지역변수를 내부에서 사용할 때, 변경하지 않는다면 final 변수로 인정해 주는 effective final 기능을 지원합니다.

응용문제

1. 다음 중 람다식에 대한 설명으로 틀린 것은 무엇입니까 ?

① 람다식은 함수형 인터페이스의 익명 구현 객체를 생성합니다.

② 매개 변수가 없을 경우 () -> {...} 형태로 작성합니다.

③ (x, y) -> {return x+y;}는 (x, y) -> x, y로 변경할 수 있습니다.

④ @FunctionalInterface가 기술된 인터페이스만 람다식으로 표형할 수 있습니다.

 

정답 : ④

2. 다음 중 람다식 표현으로 잘못되는 것을 고르세요.

① (int a, int b) -> {return a+b;};

② (s) -> System.out.println(s);

③ str -> System.out.println(str);

④ num1, num2 -> System.println(num1+num2);

 

정답 : ④

3. 다음 코드는 컴파일 에러가 발생합니다. 그 이유는 무엇이며, 어떻게 해결해야 할까요 ?

interface Calc{
	double round(int point);
}

public class LamdaExample{
	public static void main(String[] args){
    	double val = 34.1254886;
        
        Calc cal = (point) -> {
        	
            val =
            new BigDecimal(val).setScale(point, RoundingMode.HALF_UP).doubleValue();
            return val1;
        };
        System.out.println("소수점 둘쨰 자리 반올림 : "+cal.round(2));}
    }
}

val 변수를 final로 선언해서 람다식 내에서 사용할 수 있게 함

4. 두 수의 크기를 비교하는 프로그램을 만들어 사용하려고 합니다. 해당 기능을 함수형 인터페이스로 언언하고 람다식을 사용하여 구현해 보세요.

함수형 인터페이스

interface Compare{
	int compareTo(int num1, int num2);
}

 

Main class

public class CompareValueExam{
	public static void main(String[] args){
    	int num1 = 20;
        int num2 = 30;
        
      	Compare compare = (n1, n2) -> {
            if (n1 > n2) return 1;
            else if (n1 < n2) return -1;
            else return 0;
        };
        
        // 비교 결과 출력
        int result = compare.compareTo(num1, num2);
        if (result > 0) {
            System.out.println("num1이 num2보다 큽니다.");
        } else if (result < 0) {
            System.out.println("num1이 num2보다 작습니다.");
        } else {
            System.out.println("num1과 num2는 같습니다.");
        }
    }
}