람다식은 어떻게 사용할까?
람다식 (Lambda Expression)
--
람다식은
자바에서 "익명 함수"를 간단히 표현하는 방식으로,
함수형 프로그래밍을 지원하기 위해 도입된 표현 방식이다.
주로 코드의 간결성과 가독성을 높이고, 함수형 인터페이스와 함께 사용된다.
람다식 기본 문법
(매개변수) -> { 실행할 코드 }
- 매개변수 : 입력값을 지정하는 부분으로, 만약 매개변수가 하나뿐이라면 괄호()를 생략할 수 있다.
- 화살표 연산자 : 매개변수와 실행 코드를 구분한다.
- 실행 코드 : 동작할 코드를 작성하는 부분으로, 만약 한 줄 뿐이라면 중괄호{}를 생략할 수 있다.
메서드와 람다식
// 메서드
int max(int a, int b) {
return a > b ? a : b;
}
// 람다식
(a, b) -> a > b ? a : b;
과정
// 1. 메서드
int max(int a, int b) {
return a > b ? a : b;
}
// 2. 반환 타입과 이름을 지우고 화살표를 추가
(int a, int b) -> {
return a > b ? a : b;
}
// 3. 람다식
(int a, int b) -> { return a > b ? a : b; }
람다식은 익명 함수를 간단히 표현하는 방식이기 때문에 익명 함수라고 생각하면 편하다.
즉, 반환 타입과 이름만 지우고 화살표만 추가해주면 람다식이라고 할 수 있다.
(사실 정확히 람다식은 익명 함수가 아니라 익명 객체라고 보는 것이 더 정확하다.)
// 1. 람다식
(int a, int b) -> { return a > b ? a : b; }
// 2. 실행 코드(중괄호 파트)에 반환값이 존재한다면 return문 생략 가능
(int a, int b) -> { a > b ? a : b; }
// 3. 실행 코드가 한 줄뿐이기 때문에 중괄호{} 생략 가능
(int a, int b) -> a > b ? a : b;
// 4. 중괄호를 생략한 경우 세미콜론(;) 생략 가능
(int a, int b) -> a > b ? a : b
// 5. 매개변수의 타입은 컴파일러가 추론이 가능하면 생략 가능 (대부분 생략 가능)
(a, b) -> a > b ? a : b
추가적인 규칙사항
1. 매개변수가 하나인 경우에 괄호() 생략 가능하지만 타입을 생략하지 않은 경우 불가능
a -> a * a // 가능
int a -> a * a // 에러
2. 블록 안에 문장이 하나뿐이면 중괄호{} 생략 가능하지만 return문이라면 생략 불가능
(int a) -> System.out.println(a)
(int a, int b) -> { return a > b ? a : b; }
하지만 대부분 return문을 생략하여 작성하기 때문에 중괄호 생략이 가능
세미콜론(;)은 중괄호를 생략했을 경우에만 생략 가능
--
람다식과 익명 객체
--
람다식은 "익명 함수"라고도 부르지만, 보다 정확하게 말하면 "익명 객체"다.
자바의 람다식은 단순히 함수 자체를 정의하는 것이 아니라,
특정 인터페이스의 익명 구현체를 생성하는 방식으로 동작하기 때문이다.
람다식은 익명 클래스의 객체와 동등하다.
// 람다식
(a, b) -> a > b ? a : b
// 익명 객체 (위 람다식과 동일한 익명 객체)
new Object() {
int max(int a, int b) {
return a > b ? a : b;
}
}
기존에는 익명 객체처럼 작성해야 했지만
람다식을 사용하면 간단하게 작성이 가능하게 된 것이다.
객체를 다루기 위해서는 참조변수가 필요하다.
Object obj = new Object() {
int max(int a, int b) {
return a > b ? a : b;
}
}
// Objet obj = (a, b) -> a > b ? a : b; 와 동일
예시 코드
interface Test {
public abstract int max(int a, int b);
}
// 1. 인터페이스를 구현한 익명 클래스의 객체 생성 방법
Test t = new Test() {
public int max(int a, int b) {
return a > b ? a : b;
}
};
// 2. 람다식으로 표현하기 (익명 객체를 람다식으로 대체)
Test t = (int a, int b) -> a > b ? a : b;
int maxNum = t.max(3, 6); // 익명 객체의 메서드를 호출
1번(익명 클래스를 정의하여 바로 객체 생성)과 2번(람다식)은 같은 결과를 나타낸다.
2번처럼 람다식을 사용할 때
Test 인터페이스에 정의된 메서드 max()는 람다식과 메서드의 선언부(매개변수 형식)가 일치하면
대체하여 사용할 수 있다.
이렇게 대체가 가능한 이유는
람다식도 실제로는 익명 객체이고, Test 인터페이스를 구현한 익명 객체의 메서드도
max()와 람다식의 매개변수의 타입, 개수, 반환값이 일치하기 때문이다.
단, 해당 조건을 만족하기 위해서는
인터페이스에 추상 메서드가 하나만 존재해야 한다.
(람다식이 인터페이스 내부에서 어떤 추상 메서드를 대체하여 사용될지 확정 지어야 하기 때문이다.)
반면에 static메서드와 default메서드의 개수는 상관이 없다.
이렇게 람다식을 다루기 위한 인터페이스를 "함수형 인터페이스"라고 부른다.
함수형 인터페이스는
추상 메서드가 무조건 하나만 존재하는 인터페이스로, 람다식을 다루기 위한 인터페이스다.
단, static 메서드와 default메서드는 개수 상관없이 존재할 수 있다.
함수형 인터페이스 예시 코드
@FunctionalInterface
interface Test {
public abstract int max(int a, int b);
}
@FunctionalInterface는
해당 인터페이스가 함수형 인터페이스라는 것을 명시하기 위해 사용하는 것으로
정확하게는 컴파일러가 해당 인터페이스가 함수형 인터페이스의 규칙에 따라 올바르게 정의했는지 확인해 준다.
물론 꼭 작성해야 하는 것은 아니지만 붙여주면 컴파일러가 한번 더 검사를 해주게 되어 안전하게 사용할 수 있다.
--
java.util.function 패키지
--
람다식을 사용하기 위해서는 함수형 인터페이스가 필요하다.
하지만 람다식을 사용하고 싶을 때마다 매번 함수형 인터페이스를 만드는 것은 너무나도 귀찮은 일이다.
이를 해결하기 위해 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해 놓은 패키지가 존재한다.
이 패키지는 java.util.function 패키지며, 해당 패키지 안에 있는 함수형 인터페이스에 정의된 메서드 이름들이 각 형식에 알맞게 모두 정의가 되어있기 때문에 해당 패키지를 사용하여 람다식을 구현하면 메서드 이름도 통일되고,
재사용성이나 유지보수 측면에서도 좋기 때문에 되도록 해당 패키지에 존재하는 함수형 인터페이스를 사용하는 것을 권장한다.
자주 사용하는 기본적인 함수형 인터페이스
T : Type의 약자
R : Return Type의 약자
Predicate<T>는 반환타입이 boolean으로 조건식을 표현하는 데 사용된다.
이 외에도
매개변수가 2개 이상인 경우, 매개변수 타입과 반환 타입이 동일한 경우, 컬렉션을 사용하는 경우 등
다양한 함수형 인터페이스가 존재한다.
--
메서드 참조
--
만약 하나의 메서드만 호출하는 람다식이라면
"메서드 참조"를 통해 더욱 간단하게 코드를 줄여서 사용할 수 있다.
메서드 참조 형식
클래스이름::메서드이름
Integer method(String s) {
return Integer.parseInt(s);
}
// 람다식
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
// 메서드 참조로 변환
Function<String, Integer> f = Integer::parseInt;
람다식의 매개변수는 참조변수의 타입을 통해 추론이 가능하기에 생략이 가능하다.
--
'Language > Java' 카테고리의 다른 글
스레드의 동기화 (0) | 2025.01.09 |
---|---|
스레드의 상태 종류 & 제어 (0) | 2025.01.08 |
스레드 (Thread) (0) | 2024.12.29 |
제네릭 (Generics) (0) | 2024.12.28 |
Arrays 클래스에서 제공하는 메서드 (0) | 2024.12.25 |