이 글은 이것이 자바다를 참고하여 작성되었습니다.
: 익명 함수를 생성하기 위한 식
- 자바 코드가 매우 간결해지고 컬렉션의 요소를 필터링하거나 매핑해서 원하는 결과를 쉽게 집계할 수 있다.
- 람다식의 형태는 매개 변수를 가진 코드 블록이지만 런타임 시에는 익명 구현 객체를 생성한다.
Runnable runnable = new Runnable() { public void run() {...} }; // Runnable 인터페이스의 익명 구현 객체를 생성하는 코드를 람다식으로 표현하면 Runnable runnable = () -> {...}; // 이렇게 된다.
- 위 코드는 Runnable 변수에 대입되므로 람다식은 Runnable의 익명 구현 객체를 생성하게 된다.
(타입 매개변수,...) -> {실행문; ...}
- 매개변수가 없다면 람다식에서 매개 변수 자리가 없어지므로 빈 괄호를 반드시 사용해야 한다.
- 결과값을 리턴해야 한다면 return문으로 결과값을 지정할 수 있다.
(x, y) -> {return x+y;};
중괄호 안에 return문만 있을 경우에는 return문을 사용하지 않고 다음과 같이 작성하는 것이 정석이다.
(x, y) -> x+y;
- 람다식이 메소드 선언처럼 보이지만 자바는 메소드를 단독으로 선언할 수 없다. 람다식은 단순히 메소드를 선언하는 것이 아니라 이 메소드를 가지고 있는 객체를 생성해낸다.
인터페이스 변수 = 람다식;
람다식은 인터페이스 변수에 대입된다. 인터페이스는 직접 객체화할 수 없기 때문에 람다식은 익명 구현 클래스를 생성하고 객체화한다.- 람다식은 대입될 인터페이스의 종류에 따라 작성 방법이 달라진다. 람다식이 대입될 인터페이스를 람다식의 타겟 타입(target type)이라고 한다.
함수적 인터페이스(@FunctionalInterface)
- 람다식은 하나의 메소드를 정의하기 때문에 두 개 이상의 추상 메소드가 선언된 인터페이스는 람다식을 이용해 구현 객체를 생성할 수 없다.
- 함수적 인터페이스 : 하나의 추상 메소드가 선언된 인터페이스
@FunctionalInterface public interface MyFunctionalInterface { public void method(); }public class MyFunctionalInterfaceExample { public static void main(String[] args) { // TODO Auto-generated method stub MyFunctionalInterface fi; fi = () -> { String str = "method call1"; System.out.println(str); }; fi.method(); fi = () -> {System.out.println("method call2");}; fi.method(); // method() 호출은 람다식의 중괄호를 실행시킨다. } }
- 람다식의 실행 블록에는 클래스의 멤버(필드와 메소드) 및 로컬 변수를 사용할 수 있다.
- 클래스의 멤버는 제약 사항이 없지만 로컬 변수는 제약 사항이 있다.
클래스의 멤버 사용
- 클래스의 멤버를 제약 사항 없이 사용할 수 있지만 this 키워드를 사용할 때 주의가 필요하다.
- 익명 객체 내부에서 this는 익명 객체의 참조이지만 람다식에서 this는 내부적으로 생성되는 익명 객체의 참조가 아니라 람다식을 실행할 객체의 참조이다.
로컬 변수 사용
- 람다식은 메소드 내부에서 주로 작성되기 때문에 로컬 익명 구현 객체를 생성시킨다.
- 바깥 클래스의 필드나 메소드는 제한 없이 사용할 수 있으나 메소드의 매개 변수 또는 로컬 변수를 사용하면 이 두 변수는 final 특성을 가져야 한다. → 람다식에서 매개 변수 또는 로컬 변수를 읽는 것은 허용되지만 람다식 내부 또는 외부에서 변경할 수 없다.
Consumer 함수적 인터페이스
: 리턴값이 없는 accept() 메소드를 가지고 있다.
- accept() 메소드는 단지 매개값을 소비하는 역할만 한다. 사용만 할 뿐 리턴값이 없다.
Supplier 함수적 인터페이스
: 매개변수가 없고 리턴값이 있는 getXXX() 메소드를 가지고 있다.
- 실행 후 호출한 곳으로 데이터를 리턴(공급)하는 역할을 한다.
Function 함수적 인터페이스
: 매개값과 리턴값이 있는 applyXXX() 메소드를 가지고 있다.
- 매개값을 리턴값으로 매핑(타입 변환)하는 역할을 한다.
Operator 함수적 인터페이스
: 매개값과 리턴값이 있는 applyXXX() 메소드를 가지고 있다.
- 매개값을 리턴값으로 매핑(타입 변환)하는 역할보다는 매개값을 이용해서 연산을 수행한 후 동일한 타입으로 리턴값을 제공하는 역할을 한다.
Predicate 함수적 인터페이스
: 매개값과 boolean 리턴값이 있는 testXXX() 메소드를 가지고 있다.
- 매개값을 조사해서 true 또는 false를 리턴하는 역할을 한다.
디폴트 및 정적 메소드는 추상 메소드가 아니기 때문에 함수적 인터페이스에 선언되어도 여전히 함수적 인터페이스의 성질을 가지고 있다.
- 함수적 인터페이스의 성질 : 하나의 추상 메소드를 가지고 람다식으로 익명 구현 객체를 생성할 수 있는 것
- Consumer, Function, Operator 종류의 함수적 인터페이스는 andThen()과 compose() 디폴트 메소드를 가지고 있다.
- andThen()과 compose() 디폴트 메소드 : 두 개의 함수적 인터페이스를 순차적으로 연결하고 첫 번째 처리 결과를 두 번째 매개값으로 제공해서 최종 결과값을 얻을 때 사용한다.
andThen()
인터페이스AB = 인터페이스A.andThen(인터페이스B);
최종결과 = 인터페이스AB.method();
- 인터페이스AB의 method()를 호출하면 우선 인터페이스A부터 처리하고 결과를 인터페이스B의 매개값으로 제공한다.
- 인터페이스 B는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴한다.
compose()
인터페이스AB = 인터페이스A.compose(인터페이스B);
최종결과 = 인터페이스AB.method();
- 인터페이스AB의 method()를 호출하면 우선 인터페이스B부터 처리하고 결과를 인터페이스A의 매개값으로 제공한다.
- 인터페이스 A는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴한다.
- Consumer 종류의 함수적 인터페이스는 처리 결과를 리턴하지 않기 때문에 andThen() 디폴트 메소드는 함수적 인터페이스의 호출 순서만 정한다.
- Function과 Operator 종류의 함수적 인터페이스는 먼저 실행한 함수적 인터페이스의 결과를 다음 함수적 인터페이스의 매개값으로 넘겨주고 최종 처리 결과를 리턴한다.