본 포스트는 이것이 자바다을 읽고 정리한 글입니다. |
람다식
익명 함수(anonymous function)를 생성하기 위한 식으로 함수 지향 언어에 가깝다.
람다식의 형태는 매개 변수를 가진 코드 블럭이지만, 런타임 시에는 익명 구현 객체를 생성한다.
장점
- 코드가 간결해진다.
- 컬렉션의 요소를 필터링하거나 매핑하여, 원하는 결과를 쉽게 집계할 수 있다.
기본 문법
(Type parameter, ...) -> { execute; ... }
타겟 타입 & 함수적 인터페이스
Interface variable = lambda;
람다식은 인터페이스 변수에 대입된다.
= 람다식은 인터페이스의 익명 구현 객체를 생성한다.
인터페이스는 객체화하기 위하여 구현 클래스가 필요하다. |
타겟 타입
람다식이 대입될 인터페이스이다.
타겟 타입에 따라, 람다식의 작성 방법이 달라진다.
함수적 인터페이스
람다식은 하나의 메서드를 정의하기 때문에 두 개 이상의 추상 메서드가 선언된 인터페이스는 람다식을 이용하여 구현 객체를 생성할 수 없다.
함수적 인터페이스는 하나의 추상 메서드가 선언된 인터페이스를 의미하며,
함수적 인터페이스만이 람다식의 타겟 타입이 될 수 있다.
@FunctionalInterface 어노테이션
함수적 인터페이스를 작성할 때, 두 개 이상의 추상 메서드가 선언되지 않도록 컴파일러가 체킹해준다.
만약, 두 개 이상의 추상 메서드가 선언되면 컴파일 오류를 발생시킨다.
클래스 멤버 사용
람다식 실행 블럭에서 필드와 메서드는 제약 사항 없이 사용할 수 있다.
- 클래스 멤버: 필드, 메서드
주의 - this 사용
일반적으로 익명 객체 내부에서 this는 익명 객체의 참조이지만, 람다식에서 this는 내부적으로 생성되는 익명 객체의 참조가 아니라 람다식을 실행한 객체의 참조이다.
- 람다식 내부에서 this는 중첩 객체(람다식이 사용된 클래스)
- 바깥 객체의 참조를 얻기 위해서는 클래스(바깥 객체의 클래스)명.this 를 사용
로컬 변수 사용
람다식 바깥 클래스의 필드나 메서드는 제한 없이 사용할 수 있다.
final 특성
단, 메서드의 매개 변수 또는 로컬 변수를 사용하면 두 변수는 final 특성을 가져야 한다.
이유: 9.5.3장 익명 객체의 로컬 변수 사용 참고
매개 변수 / 로컬 변수
- 읽는 것은 허용
- 람다식 내/외부에서 변경 불가능
메서드 참조
매개 변수 정보 및 리턴 타입을 알아내, 불필요한 매개 변수를 제거한다.
메서드를 단순히 호출만 하는 경우, 메서드 참조를 이용하여 깔끔하게 나타낼 수 있다.
Class :: method
ex.
(l, r) -> Math.max(l, r);
// to
Math :: max;
이 역시 인터페이스의 익명 구현 객체로 생성된다.
따라서, 타겟 타입인 인터페이스의 추상 메서드가 어떤 매개 변수를 가지고, 리턴 타입이 무엇인가에 따라 달라진다.
정적(static) 메서드
// (x, y) -> Class.staticMethod(x, y)
Class :: staticMethod
인스턴스 메서드
객체 생성 후 메서드 참조가 가능하다.
Class referenceVariable = new Class();
// (x, y) -> referenceVariable.instanceMethod(x, y);
referenceVariable :: instanceMethod
매개 변수 메서드 참조
a 매개 변수의 메서드를 호출해서 b 매개 변수를 인자로 사용할 때도 메서드 참조가 가능하다.
// (a, b) → { a.instanceMethod(b); }
a's Class :: instanceMethod
생성자 참조
객체 생성을 의미한다.
단순 객체 생성 후 리턴을 생성자 참조로 대치할 수 있다.
// (a, b) -> { return new Class(a, b); }
Class :: new
생성자가 오버로딩되어 여러 개가 있을 경우, 컴파일러는 동일한 매개 변수 타입/개수를 가진 생성자를 찾아 실행한다.
이 때, 해당 생성자가 존재하지 않으면 오류가 발생한다.
표준 API의 함수적 인터페이스
한 개의 추상 메서드를 가지는 인터페이스들은 모두 람다식을 이용해서 익명 구현 객체로 표현이 가능하다.
자바 8부터는 자주 사용되는 함수적 인터페이스를 java.util.function 표준 API 패키지로 제공한다.
위 패키지의 함수적 인터페이스는 추상 메서드의 매개값과 리턴값의 유무에 따라 크게 5가지로 구분된다.
- Consumer, Supplier, Function, Operator, Predicate
Overview
종류 | 추상 메서드 특징 |
Consumer | 매개값: O / 리턴값: X |
Supplier | 매개값: X / 리턴값: O |
Function | 매개값: O / 리턴값: O - 주로 매개값을 리턴값으로 매핑 (타입 변환) |
Operator | 매개값: O / 리턴값: O - 주로 매개값을 연산하고 결과 반환 |
Predicate | 매개값: O / 리턴값: O (boolean) - 매개값을 조사하여 true/false 반환 |
표준 API의 함수적 인터페이스에 대한 내용은 추가 포스트 게시할 예정입니다.
사용 예시
현재 참여 중인 CoNet 프로젝트의 AuthSerivce에 작성된 코드이다.
userId -> { // 이미 회원가입과 약관 동의 및 이름 입력이 모두 되어있는 유저
User findUser = userDao.findById(userId).orElseThrow(() -> new UserException(NOT_FOUND_USER));
// 회원가입은 되어있는데, 약관 동의 혹은 이름 입력이 되어있지 않은 유저
if(!findUser.getOptionTerm() | findUser.getName() == null) {
return getLoginResponse(findUser, clientIp, false);
}
return getLoginResponse(findUser, clientIp, true);
}
() -> { // 회원가입이 필요한 유저
User oauthUser = new User(email, platform, platformId);
User saveduser = userDao.save(oauthUser).get();
return getLoginResponse(saveduser, clientIp, false);
}