진짜 개발자
본문 바로가기

Language/Java

Java - Lambda Expression(람다식)이란?

728x90
람다식이란

람다식이란 무엇일까요. 그 동안 java를 이용하다가 lambda식 이란 것을 처음 보았을때는 매우 생소하고 거부감이 느껴졌습니다. 하지만, java8에서 추가된 stream과 같은곳에서 한두번씩 사용하기 시작해보니 정말 편리한 기능임을 알게 되었습니다.

( 한편에서는 너무 많은 코드를 생략하다보니 오히려 프로그램의 가독성을 저해시킨다는 의견도 있습니다. )

 

 

1. 람다식이란?

java 8부터 추가된 기능으로 익명객체를 생성하기 위한 표현식을 말합니다. 무슨말일까요.. 역시 예제를 살펴보는것이 더 나을것 같습니다.

 

1.1 람다식 이전

기존 자바에서 interface를 이용해 다형성을 제공하기 위해서는 interface를 만들고, 그것을 구현한 class를 작성한 뒤, 사용시에는 interface타입의 참조변수에 interface를 구현한 class의 객체를 생성하여 사용했습니다.

 

간단하게 흐름을 살펴보면 이러한 흐름을 가지는것 같습니다.

 

또는 위와 같이 실행하는 쪽에서 익명 객체를 만들어 사용했습니다. 하지만 Goods를 구현한 객체가 자주 사용되어야 한다면, 위의 코드를 계속해서 반복해서 사용해야할 것이고 그렇게 되면 지저분한 코드가 될 것입니다.

 

1.2 람다식 사용

하지만 람다식을 사용하는 경우 위와 같이 아주 간결하게 표현을 할 수 있습니다.

 

람다식을 이용한 흐름은 이전보다 훨씬 간단하게 변경된것 같습니다.

 


 

2. 람다식 표현법

람다식은 매개변수 + 실행문으로 구성됩니다. 즉 접근자, 반환형 모두 생략되는 구조 입니다.

 

모양은 ()->{};의 형태로 구성됩니다. 첫 괄호 () 해당 interface 함수의 매개변수를 입력하면 됩니다. 그 다음 ->를 입력하고, {}(중괄호) 안에 실행할 코드를 작성하면 됩니다.

 

2.1 예제

아래의 람다식에서 사용될 인터페이스 입니다.

 

1. 기본 사용법((매개변수 타입)->{};)

람다식에 필요한 기호를 모두 사용한 방법입니다.

 

2. 매개변수 타입 생략((매개변수)->{};)

매개변수가 1개 이거나 2개 이상의 매개변수의 타입이 모두 같을 때에는 타입을 생략할 수 있습니다.

 

3. 매개변수가 없는 경우(()->{};)

만약 Calculator인터페이스의 cal() 메소드에 매개변수가 없다면 위와 같이 매개변수를 생략하여 작성할 수 있습니다.

 

4. 중괄호 생략(()->;)

실행할 문장이 1개일 때에는 {}(중괄호)를 생략할 수 있습니다. 이 때 중요한 점은 반환이 필요한 메소드의 경우return 키워드를 생략해야 한다는 것 입니다.

 

5. 소괄호 중괄호 생략(매개변수 -> ;)

public static void main(String[] args) {
	Calculator cal = num1 -> System.out.println(num1);		
	cal.cal(1);
}

매개변수가 1개이고 실행할 문장도 1개이면 위와 같이 (){}를 생략할 수 있습니다.

 


 

3. 람다 사용 조건

람다식을 사용하기 위한 인터페이스에는 조건이 있습니다. 바로 구현해야할 인터페이스의 추상 메소드가 단1개 이어야 한다는 조건입니다. 2개 이상인 경우 어떤 메소드를 람다식으로 표현했는지 알 수 없으니 당연하지 않냐구요? 예 사용하는 입장에서는 당연합니다. 사용하는 입장이라는게 무슨 말일까요? 그럼 사용하는 입장의 반대의 경우는 누구일까요? 바로 인터페이스를 설계 또는 작성하는 사람일 것입니다.

 

람다식으로 사용하기 위한 인터페이스를 만들었고, 잘 사용하고 있었습니다. 하지만 해당 인터페이스를 구현하는 클래스에서 또 다른 기능이 필요하게 되었고 개발자가 해당 인터페이스에 메소드를 추가했습니다. 어? 그런데 사용하는 곳에서 갑자기 에러가 나기 시작합니다. 개발자는 이제서야 해당 인터페이스가 람다식을 위한 단일 추상메소드를 가진 인터페이스였음을 알게 되었습니다.

 

//Object 객체의 메소드만 인터페이스에 선언되어 있는 경우는 Functional Interface가 아님
public interface NotFunctional {
    public boolean equals(Object obj);
}
 
//Object 객체의 메소드를 제외하고 하나의 추상 메소드만 선언되어 있는 경우는 Functional Interface임
public interface Functional {
    public boolean equals(Object obj);
    public void execute();
}
 
//Object객체의 clone 메소드는 public 메소드가 아니기 때문에 Functional Interface의 대상이 됨
public interface Functional {
    public Object clone();
}
public interface NotFunctional {
    public Object clone();
    public void execute();
}

출처 - http://wiki.sys4u.co.kr/pages/viewpage.action?pageId=7766426

이뿐만이 아닙니다. 람다식을 위한 인터페이스를 작성하기 위해서는 지켜야할 규칙이 더 있습니다. 개발자가 이것을 모두 기억한 상태로 모든 인터페이스들을 검사하며 개발하기에는 너무 어렵습니다. 물론 인터페이스 이름으로 구분하는 것도 좋은 방법입니다. 하지만 이보다 더 확실한 방법이 있습니다.

 

 

3.1 @FunctionalInterface Annotation

바로 @FunctionalInterface 어노테이션을 사용하는 것 입니다.

 

위와 같이 람다식으로 표현하기 위해 사용될 interface위에 @FunctionalInterface만 붙혀준다면 컴파일 타임에 알아서 에러를 잡아줍니다. 위의 그림에서 Interface에 메소드를 2개 작성하여 에러가 표시된것을 알 수 있습니다.