티스토리 뷰

JAVA/정리

[Lambda Expression] 람다식

란텔 2016. 5. 2. 19:31
람다식은 메서드를 하나의 식(expression)으로 표현한 것.

람다식을 '익명 함수'라고도 한다.



람다식의 기본 구문은 다음과 같다.

//일반적인 메서드의 형식
int sum(int a, int b){
return a+b;
}
//람다식을 사용한 경우
(int a, int b) -> { return a+b; };

메서드 선언부의 메서드명이 없어지고 선언부와 구현부 사이에 화살표 표시가 존재한다. 



람다식은 그 자체는 메서드 모습을 하고 있지만 엄밀히 말하면 익명 클래스의 객체와 같다고 볼 수있다.

Test t = new Test(){
     int sum(int a, int b){
          return a+b;
     }
};

익명 클래스의 객체라는게 위에서 보는 것 처럼 인스턴스 생성 즉시 그 클래스 타입의 멤버를 바로 기술하는 것인데 이 익명클래스 객체가 가리키는 것이 참조형 타입의 변수이다.

그리고 이 변수는 참조형 타입의 클래스는 추상클래스나 인터페이스가 될 수 있다.



람다식을 사용하기 위해서도 어떤 타입의 참조변수를 가리키고, 그 참조변수를 통하여 람다식을 사용하게끔 해야 하는데..

그것을 지원해 주는 것이 바로 '함수형 인터페이스'라는 것이다.

@FunctionalInterface
interface CustomLambda{
    int operator(int a, int b);
}

람다식이 참조 하는 대상이 되기 위한 '함수형 인터페이스'의 조건은 다음과 같다.

1. 람다식과 동일한 매개변수를 취하는 메서드가 있어야 한다.

2. 메서드는 하나만 있어야 한다.

메서드가 하나인 이유는 람다식의 대상이 되는 메서드가 다음과 같이 2개 이상이라면..

interface CustomLambda{ int operator(int a, int b); int test(int a, int b); void method(int b); }

(a, b) -> { return a+b }; 라는 람다식과 대응되는 것을 찾지 못할 것이기 때문에 메서드를 하나로 제한하는 것이다.


@FunctionalInterface노테이션을 사용하면 자동적으로 함수형 인터페이스인지 아닌지를 체크해준다. 이 어토테이션을 정의해 놓은 메서드 작성 시 '함수형 인터페이스'의 조건에 위배 된다면, 컴파일시 에러가 발생한다.



아래는 위의 '함수형 인터페이스'에 대응되는 람다식을 작성해 본 것이다.

CustomLambda sum = (int a, int b) -> { return a+b; }; //람다식의 구현부 대괄호 안에 return문이 존재하면 대괄호를 생략할 수 없다. CustomLambda sub = (a,b) -> a-b; //선언부 매개변수의 타입을 생략할 수 있으며, 어느 한쪽만 생략할 수는 없다. //구현부의 문장이 연산식 하나뿐일때는 그 자체가 리턴값이 되고 대괄호를 생략한다. CustomLambda mul = (a,b) -> a*b; CustomLambda exlong = (a,b) -> { if(a > b){ return a; } else{ return b; } }; //위처럼 구현부가 한 문장의 연산식이 아닌 여러줄의 코드를 취한다면 {}대괄호를 생략할 수 없다. System.out.println("sum.operator(1, 2)>>>>>"+sum.operator(1, 2)); System.out.println("sub.operator(1, 2)>>>>>"+sub.operator(1, 2)); System.out.println("mul.operator(1, 2)>>>>>"+mul.operator(1, 2));

sum변수는 더하기, sub변수는 빼기, mul변수는 곱하기 기능을 하도록 간단하게 작성해 보았다. 이처럼 선언부의 매개변수의 갯수와 타입만 일치한다면 서로 다른 기능을 하는 람다식을 작성 할 수있다.




다시 '함수형 인터페이스' 얘기를 하자면 람다식이 함수형 인터페이스를 참조하는 것일 뿐, 타입이 일치하는 것은 아니다. 람다식은 익명 객체이고 컴파일러에 의해서 타입의 이름이 결정지어진다. 그래서 실제로는 다음과 같이 형변환이 필요하지만...

CustomLambda sum = (CustomLambda)(int a, int b) -> { return a+b; };

이(형변환)는 생략이 가능한 것이다.



CustomLambda sum = (CustomLambda)(int a, int b) -> { return a+b; }; System.out.println(sum); //람다식 객체의 참조 변수 System.out.println(new Common()); //일반 객체의 참조 변수

람다.LamdaTest1$$Lambda$1/834600351@548c4f57 람다.Common@1218025c

컴파일러가 람다식 타입의 인스턴스를 어떻게 표현하는지를 알 수 있다.

일반 클래스 타입클래스명@숫자문자

람다식패키지명.클래스명$$Lambda$숫자@숫자문자

와 같이 표현함을 알 수 있다.




람다식 내부에서 지역변수를 차용할 경우..


- 지역변수를 람다식에서 사용할 수 있지만 값의 변경을 허용하지 않는다.(람다식 내부건 메소드 내부건 값 변경 허용 안함)

- 지역변수를 람다식에서 사용하면 더 이상 변경 불가능한 상수가 된다.(final이 없어도 상수가 됨)






java.util.function 패키지에는 재사용성과 유지보수성을 이유로 자주 쓰일만 한 여러가지 '함수형 인터페이스'를 제공하고 있다.

그러니 무분별한 '함수형 인터페이스를' 만드는 것 보다 이 패키지를 이용해서 람다식을 다루는 것이 좋다.

https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html



그 중에서 Predicate '함수형 인터페이스'를 보면

Predicate<T>   Represents a predicate (boolean-valued function) of one argument.

하나의 인자를 가지고 boolean형 변수를 리턴값으로 가짐을 알 수 있다.


구현되어 있는 메서드는 boolean test(T t);인데 매개 변수의 유효성을 검사하는데 사용하면 좋을 것 같다.

        String str = null;
        Predicate<String> p = (s) -> s == null;
        
        
        System.out.println("문자열 str은 null입니까??? "+p.test(str));
        
        //결과>>>>>>>>>>>문자열 str은 null입니까??? true






'JAVA > 정리' 카테고리의 다른 글

[Stream] 스트림의 정렬(sorted), 요소 변환(map), 배열요소를 요소로 변환(flatMap)  (0) 2016.07.16
[Stream] 스트림 part1  (0) 2016.07.16
[generics] 제네릭스  (0) 2016.04.21
익명 클래스  (0) 2016.04.07
Thread  (0) 2016.01.14
Comments
최근에 올라온 글
최근에 달린 댓글
TAG
more
Total
Today
Yesterday