개발/Java

[Java] 람다(Lamda)표현식

김현중 (keemhing) 2024. 8. 12. 21:01

람다식 (Lamda expression)

람다식은 1930년대 알론조 처치(Alonzo church)라는 수학자가 처음 제시한 함수의 수학적 표기방식인 람다 대수에 그 뿌리를 두고 있다. Java 8부터 람다식이 도입되었으며 Java에서는메서드를 간결한 함수식으로 표현한 것을 말한다. 자바에서 메서드를 하나 표현하기 위해서는 클래스를 정의해야만 했다. 하지만 람다식으로 표현하면 메서드의 이름과 반환값을 생략할 수 있고 이를 변수에 넣어 자바 코드가 매우 간결해지는 장점이 있다. 반면에 모든 엘리먼트를 순회하는 경우 성능이 떨어질 수 있고, 코드를 분석하기 어렵다는 단점이 있다. 그렇다면 어떻게 생략하면 될까? 하나씩 알아가 보자.


 

화살표 기호(->) 사용

메서드 표현식을 메서드 타입, 메서드 이름, 매개변수 타입, 중괄호, return문을 생략하고 화살표 기호를 넣어 코드를 단축시킨다. 이러한 특징으로 람다식을 이름이 없는 익명함수(anonymous function)이라고도 한다.

// add 메서드 정의
int add(int a, int b) { return a+b;}

// add 메소드를 람다 표현식을 이용하여 단축 시킬 수 있다. (메서드 반환 타입, 메서드 이름 생략)
(int a, int b) -> { return a+b; };

// 매개변수 타입도 생략 가능하다.
(a, b) -> { return a+b; };

// 함수에 리턴문 한 줄만 있을 경우 더욱 단축시킬 수 있다. (중괄호, return 생략)
(a, b) -> a+b;

 

이게 뭔가 싶다 솔직히.... ㅋㅋㅋ 눈에도 잘 안 들어오고 어색하다. 하지만 문법이 혁신적으로 간결해지는 만큼, 람다식에 익숙해지면 가독성 면에서 큰 장점을 얻는다고 한다. 특히 컬렉선(Collection)의 요소를 필터링하거나 매핑하여 원하는 결과를 얻을 수 있다고 한다


값을 반환하지 않는 메서드 구현 람다식

람다식의 형태를 보면 자바의 메서드를 변수로 선언하는 듯 보이지만, 자바는 메소드를 단독으로 선언할 수 없다. 형태만 그렇게 보일 뿐, 코드를 보면 람다 함수식을 변수에 대입하고 변수에서 메서드를 호출해서 사용하는 것이 마치 객체와 다름없다. 사실 람다식도 객체이다. 정확히 말하면 인터페이스를 익명 클래스로 구현한 익명 구현 객체인 것이다..

interface Printable {
	void print(String s); // 매개변수 하나, 반환타입 void
}

 class OneParamNoReturn {
	public static void main(String args[]){
    	Printable p; // 객체 생성
        p = (String s) -> {System.out.println(s);} // 줄임 없는 표현
        p.print("Lamda exp one.");
        
        p = (String s) -> System.out.println(s); // 중괄호 생략
        p.print("Lamda exp two.");
        
        p = (s) ->  System.out.println(s); // 매개변수 형 생략
        p.print("Lamda exp three.");
        
        p = s -> System.out.println(s); // 매개변수 소괄호 생략
        p.print("Lamda exp four.");
    }
}

 

위 코드에서 보이듯 줄임이 없는 람다식은 다음과 같다. 매개변수 정보에 소괄호를 하고 메소드 몸체에 중괄호를 한다.

그러나 메서드의 몸체가 하나의 문장으로 이뤄져 있다면 다음 코드같이 중괄호의 생략이 가능하다. 단, 중괄호를 생략할 때 해당 문장의 끝에 위치한 세미콜론도 함께 지워야 한다. 그리고 매개변수 정보에 있어서 s가 String형 임은 컴파일러 입장에서 해당 람다식이 채우게 될 메소드 정보를 통해서 유추가 가능하다. 따라서 세번째 코드처럼 매개변수의 자료형 정보도 생략 가능하다. 그리고 매개변수가 하나일 경우에는 마지막 코드처럼 소괄호도 생략할 수 있다. 메소드의 몸체가 둘 이상의 문장으로 이뤄져 있거나, 매개변수의 수가 둘 이상인 경우에는 각각 중괄호와 소괄호의 생략이 불가능하다.

interface Calculate {
	void cal (int a, int b); // 매개변수 둘, 반환타입 void
}

class TwoParamNoReturn {
	public static void main(String args[]) {
    	Claculate c;
        c = (a, b) -> System.out.println(a + b);
        c.cal(4, 3); // 덧셈 진행
        
        c = (a, b) -> System.out.println(a - b);
        c.cal(4, 3); // 뺄셈 진행
        
        c= (a, b) -> System.out.println(a * b);
        c.cal(4, 3); // 곱셈 진행
    }
}

값을 반환하는 메소드 구현 람다식

 

값을 반환하는 메서드를 구현하는 람다식의 예제를 보자.

interface Calculate{
	int cal(int a, int b);	// 값을 반환하는 추상 메서드  
}

class TwoParamAndReturn {
	public static void main(String args[]){
    	Calculate c;
        c = (a, b) -> { return a + b; };
        System.out.println(c.cal(4, 3));
       
        c = (a,  b) -> a + b;
        System.out.println(c.cal(4, 3));
    }
}

 

실행결과는 첫 번째 7, 두 번째 7이 나온다. 첫번째 람다식을 보면 메서드 몸체에 해당하는 내용이 return 문이면 그 문장이 하나이더라도 중괄호의 생략이  불가능하다. 그러나 두번째 람다식과 같이 표현이 가능하다. 그 이유는 연산이 진행되면 결과로 값이 남게 되는데, 이 값은 별도로 명시하지 않아도 반환의 대상이 된다. 따라서 return 문이 메소드 몸체를 이루는 유일한 문장이면 위와 같이 작성할 수 있다. 다른 예시를 보자.

interface HowLong{
	int len(String s);  // 값을 반환하는 메서드
}

class OneParamAndReturn  {
	public static void main(String args[]) {
    	HowLong hl = s -> s.length();
        System.out.println(hl.len("happy"));
    }
}

실행결과로 5가 나온다. 위 예시에 등장한 람다식은 메소드 몸체를 이루는 유일한 문장이 메소드 호출문인데, 이 문장에서 호출하는  length는 값을 반환한다. 따라서 메서드의 호출 결과로 반환된 값이 남는다. 그리고 이렇게 반환된 값 역시 별도로 명시하지 않아도 반환의 대상이 된다. 따라서 다음과 같이 람다식을 작성할 필요가 없다.

s -> { return s.length(); }

매개변수가  없는 람다식

매개변수가 없는 람다식은 매개변수를 표현하는 소괄호 안을 비우면 된다.

import java.util.Random;

interface Generator {
	int rand(); // 매개변수 없는 메소드
}

class NoParamAndReturn {
	public static void main(String args[]) {
    	Generator gen = () -> {
        	Randon rand = new Random();
    		return rand.nextInt(50);
        }
    };
    
    System.out.println(gen.rand());
}

 

실행 결과로는 49가 나온다. 위 람다식은 매개변수 선언이 없는 관계로 매개변수 정보를 담는 소괄호가 비어있다. 그리고 둘 이상의 문장으로 이뤄진 람다식은 중괄호로  반드시 감싸야하며, 값을 반환할 때에도 return문을 반드시 사용해야 한다.


람다식과 함수형 인터페이스

위 람다식 예제들에는 공통점이 있다. "정의되어 있는 인터페이스에는 추상 메서드가 딱 하나만 존재한다." 이러한 인터페이스를 가리켜 '함수형 인터페이스(Functional Interface)'라고 한다. 람다식은 이러한 함수형 인터페이스를 기반으로만 작성이 될 수 있다. 


람다는 남발하면 코드가 지저분해질 수 있으며, 코드 자체로 동작이 명확하게 설명되지 않거나 읽기 어렵다면 쓰지 않는 것이 좋다. Java에 대해 많이 공부하여 필요한 때에 적절하게 사용할 수 있도록 공부해야겠다. 

 

<참고 자료>

1. Inpa Dev
2. 윤성우의 열혈 Java 프로그래밍