JAVA8 Lambda 람다는 @FunctionalInterface의 구현체이다.

JAVA8 Lambda람다는 @FunctionalInterface의 구현체이다.


JAVA7이 나온지 약 10년만에 JAVA8이 나왔다. JAVA8에서의 다양한 변화중 가장큰 변화는 Lambda표현식을 통한 Funtional Programming (함수형 프로그래밍)을 지원한다는 점이다. 다른 스크립트 언어들과는 달리 JAVA는 method타입을 선언할 수 없었다. 그렇다면 JAVA는 어떻게 함수형 프로그래밍을 지원하는 걸까?


Lamda 람다란?

람다식(Lambda Expression)은 메소드를 직접 정의하지 않고 하나의 '식(Expression)'으로 표현한 것이다. 일반적인 메소드 블럭은 리턴타입, 메소드명, 파라메터정의 3부분으로 이루어져 있다. 여기서 생략이 가능한 리턴타입, 메소드명을 제거하면 Lambda표현식이 된다.

메소드블럭
public int someMethod(int a, int b) {
return a+b;
}

람다표현식
public int someMethod(int a, int b) -> {
return a+b;
}

리턴타입인 int와 메소드명 someMethod가 제거되어 파라메터 정의와 {소스코드블락}만 남게 되었고 그 사이를 -> 로 연결했다. '->' 표시가 람다 표현식임을 컴파일러에게 알려준다. 생략 된 리턴타입은 컴파일러가 유추가 가능하기 때문에 명시하지 않아도 된다. 마찬가지로 원리로 입력 파라메터도 소스코드 문맥상 생략이 가능하다면 생략할 수 있다.

위에 람다표현식은 아래와같이 더욱 생략가능하다.
(int a, int b) -> {return a+b};
(int a, int b) -> a+b;
(a, b) -> a + b;

만약 매개변수가 하나라면 매개변수의 괄도 생략가능하다.
a -> a + 1;

 Lamda 사용예제

List출력
List<String> list = Arrays.asList(new String[]{"a", "b", "c"});
for(int i = 0 ;  i < list.size() ; i++ ) {
System.out.println(list.get(i));
}

Lambda를 이용한 List출력
list.forEach(x -> System.out.println(x));

Thread Runnable 구현
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
t.start();

Lambda를 이용한 Runnable구현
Thread t = new Thread(()->System.out.println(Thread.currentThread().getName()));
t.start();

여기서 주목해야 하는 것은 Runnable Interface의 구현체( new Runnalbe()...) 대신에 람다표현식( ()-> System.out.... ) 로 대체되었다는 점이다. 이것이 가능한 이유는 JAVA8부터 Runnable Interface에 @FunctionalInterface 어노테이션이 추가 되었기 때문이다.

interface Runnable in JDK 8
@FunctionalInterface
public interface Runnable {
...

FuntionalInterface란?

인터페이스는 원래 메소드의 "선언"만 갖을 수 있다. 그런데 JDK8부터 인터페이스도 default 접근제한자와 함께 메소드 "구현"도 갖을 수 있게 되었다. 이 때, FuntionalInterface는 오직 하나의 메소드 선언을 갖는 인터페이스를 말한다.

FunctionalInterface 예시
@FunctionalInterface
interface MyFunction {
void methodA(); // 오직하나의 메소드 선언
// 이런건 메소드 구현 까지 포함
default void methodB(){ 
System.out.println("Call Method B");
}
}

이때 MyFunction 오브젝트를 파라메터로 받는 다음과 같은 method가 있다면
public static void test(MyFunction f) {
f.methodA();
}

구현체를 생성하여 호출
MyFunction f = new MyFunction() {
@Override
public void methodA() {
System.out.println("Call Method A");
}
};

test(f);

Lambda표현식으로 호출
test(()->{System.out.println("Call Method A");});

위에 두개 패턴은 동일한 소스이다. 즉 Lambda 표현식은 인터페이스의 구현체와 동일한 의미를 갖는다.
이렇게 하면 더욱 명확해 진다.
MyFunction f = ()->{System.out.println("Call Method A");};
test(f);

결론

Ladda표현식은 최종적으로 @FunctionalInterface의 구현체로 생성되는 오브젝트이다. 개념이나 표현이 낯설긴 하지만 기본적으로 인터페이스의 구현체라고 생각하고 개발하면 된다.


이 글을 공유하기

댓글

Email by JB FACTORY