1) 람다 표현식이란?
- 람다 표현식(이하 람다식)은 함수를 간단한 '식(expression)'으로 표현한 것을 의미합니다.
람다식을 작성하는 법은 반환 타입과 이름을 지우고, ->를 블록 앞에 추가하면 됩니다.
람다식은 함수명을 가지지 않는데, 이러한 특징을 익명 함수(anonymous function)라고 합니다.
int max(int a, int b){
return a > b ? a : b; => (a, b) -> a > b ? a : b
}
2) 람다식 작성 시 주의사항
- 람다식을 작성할 때는 몇 가지 주의사항이 있습니다.
(1) 매개변수가 하나인 경우에는, 괄호() 생략이 가능합니다.
단, 타입이 없을 때만 가능합니다.
(a) -> a * a => a -> a * a // OK
(int a) -> a * a => int a -> a * a // 에러
(2) 블록 안의 문장이 하나일때는, 괄호{} 생략이 가능합니다.
(int i) -> { System.out.println(i) } => (int i) => System.out.println(i)
3) 람다식 예
Comparator<Apple> byWeight = new Comparator<Apple>(){
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
};
위의 코드를 람다 표현식으로 나타내면 다음과 같이 나타낼 수 있습니다.
Comparator<Apple> byWeight =
(Apple a2, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
람다 표현식으로 나타냄으로 인해서, 코드가 훨씬 간결해지고 가독성이 좋아졌습니다.
람다 표현식의 구성 요소를 분해해서 설명하면 다음과 같습니다.
(Apple a2, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
람다 파라미터 화살표 람다 바디
람다 파라미터 - 람다 표현식의 메서드 파라미터입니다.
화살표 - 람다의 파라미터 리스트와 바디를 구분합니다.
람다 바디 - 람다의 반환 값에 해당하는 표현식입니다.
(1-1) 함수형 인터페이스
- 람다 표현식은 함수형 인터페이스를 선언해서 사용할 수도 있습니다.
함수형 인터페이스는 @FunctionalInterface라는 어노테이션을 사용하고,
하나의 추상 메서드만을 허용합니다.
다음은 함수형 인터페이스를 예시 코드로 살펴보겠습니다.
@FunctionalInterface
public interface Calculate{
public void operation(int a, int b);
}
public void calculateLambda(){
Calculate calculateAdd = (a, b) -> a+b;
calculateAdd.operation(a,b);
Calculate calculateSubtract = (a,b) -> a-b;
calculateSubtract.operation(a,b);
}
(1-2) java.util.function 패키지
- Java 8에서는 java.util.function이라는 Functional 인터페이스를 위한 패키지를 제공합니다.
이 패키지가 제공하는 대표적인 Functional 인터페이스로는 Predicate, Consumer, Function 등이 있습니다.
(1) Predicate
- Predicate은 test라는 추상 메서드를 정의하고, test 메서드는 제너릭 형식의 T의 객체를 인수로 받아,
이를 boolean 값으로 반환합니다.
Predicate의 예시 코드는 다음과 같습니다.
@FunctionalInterface
public interface Predicate{
boolean test(T t);
}
public <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> results = new ArrayList<>();
for(T t: list){
if(p.test(t){
result.add(t);
}
}
return result;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
(2) Consumer
- Consumer 인터페이스는 제네릭 형식의 T 객체를 받아서, void를 반환하는 accept 추상 메서드를 정의합니다.
즉, T 형식의 객체를 받아서 어떤 동작을 수행하고 싶을 때, Consumer 인터페이스를 사용할 수 있습니다.
Consumer 인터페이스의 코드는 다음과 같습니다.
@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
}
public <T> void forEach(List<T> list, Consumer<T> c){
for(T t: list){
c.accept(t);
}
}
forEach(
Arrays.asList(1,2,3,4,5),
// Consumer의 accept 메서드를 구현하는 람다 표현식
(Integer i) -> System.out.println(i);
);
(3) Function 인터페이스
- Function 인터페이스는 T 제네릭 객체를 받아서, R 제네릭 객체를 반환하는 apply 추상 메서드를 갖고 있습니다.
@FunctionaInterface
public interface Function<T, R>{
R apply(T t);
}
public <T, R> List<R> map(List<T> list, Function<T, R> f){
List<R> result = new ArrayList<>();
for(T t: list){
result.add(f.apply(t));
}
}
List<Integer> l = map(
Arrays.asList("lambdas", "in", "action");
// Function 인터페이스의 apply 메서드를 구현하는 람다 표현식
(String s) -> s.length();
- 2편에서는 Java 8 함수형 프로그래밍의 다른 주제인 스트림에 대해 다뤄보겠습니다.
참고
자바의 신 2권
모던 자바 인 액션
'Java' 카테고리의 다른 글
자바 float과 double 자료형 (0) | 2022.07.31 |
---|---|
자바 int와 long 자료형 (0) | 2022.07.31 |
Java HashMap 이해하기 (0) | 2022.07.01 |
Object 클래스 (0) | 2022.06.14 |
Collection 인터페이스 (0) | 2022.05.30 |