2021. 12. 20. 20:14ㆍ개발/Java
Lambda 표현식은 Java 8 버전에서 정식 발표되었다. 함수형 프로그래밍을 지원하기 위해 Java가 첫번째로 도입한 것이 바로 Lambda 표현식이다.
함수형 프로그래밍을 공부하다 보면 Pure Function(순수 함수), higher-order function(고계 함수) 등 다양한 함수들을 만나게되는데 Lambda는 그중 anonymous function(익명 함수)의 특성을 가지고 있다. 따라서 우리가 알고 있는 일반적인 함수와 달리 Lambda는 함수의 이름이 없다.
Lambda는 Class에 속하지 않고도 그 자체로 생성이 가능하다는 주요한 특징이 있다. 또한, 변수처럼 파라미터로 전달하거나 특정 요청에 의해 언제든지 실행될 수 있다.
Lambda는 간단한 이벤트 리스너나 콜백함수로 사용 되기도 하지만 아마 Stream API에서 가장 많이 사용될것이다.
( 구글링하면 나오는 대부분의 Lambda 예제가 Stream API이다. )
예제를 통해서 lambda 사용법을 알아보자. lambda는 주로 인터페이스의 익명 구현 객체와 Stream API에서 주로 사용된다.
1. Anonymouse Class vs. Lambda
Anonymouse Class(익명 구현객체)는 인터페이스를 일회성으로 작성된 익명의 객체가 구현하는 것을 말한다. 인터페이스를 구현하는데 별도의 Class가 없기때문에 익명 구현객체라는 이름이 붙은듯하다. Lambda를 사용하면 익명 객체 구현방식 보다 좀더 코드를 간결하게 짤 수 있다.
먼저 Print 인터페이스를 정의한다. 구현해야하는 메소드는 1개이다.
public interface Printer {
// content 출력을 위한 메소드
void printHello(String content);
}
User 클래스를 정의한다. setPrinter를 통해서 Printer 인터페이스에 대한 구현체를 주입받는다.
public class User {
private Printer printer;
// Printer 구현체를 주입받는다.
public void setPrinter(Printer printer){
this.printer = printer;
}
public Printer getPrinter() { return printer; }
}
아래는 Printer 인터페이스의 구현체를 User에 주입하는 2가지 방법이다. 둘다 별도의 구현 Class가 필요없다. 간단하고 재사용 가능성이 낮은 인터페이스를 구현해야할 때 사용하면 좋다.
Lambda 방식이 좀더 간결한 코드를 가지고 있고, 익명 구현객체는 구현 메소드명을 명시적으로 확인할 수 있다는 점이 특징이다. IntelliJ에선 Labmda 방식을 사용하도록 유도하고 있었다. 다만 구현해야하는 메소드가 2개 이상인 경우(인터페이스에 2개 이상의 메소드가 정의)있는경우 lambda는 사용이 불가능하다.
public class Main {
public static void main(String[] args){
User user = new User();
// 1. 익명 구현객체 방식
user.setPrinter(new Printer() {
@Override
public void printHello(String content) {
System.out.println("[Anonymous interface] content = " + content);
}
});
user.getPrinter().printHello("soojong");
// 2. lambda 방식
user.setPrinter(content -> {
System.out.println("[lambda] content = " + content);
});
user.getPrinter().printHello("soojong");
}
}
2. Stream
아마도 Lambda 표현식이 가장 많이 사용되는 곳이 바로 Stream API일 것이다. Stream만 해도 배울게 많으니 여기선 필터링과 변환 정도만 기록한다.
Stream에서 지원하는 다양한 기능과 Lambda 표현식을 통해 필터링이나, 데이터 변환 작업을 간결하게 수행할 수 있다.
확실히 for-loop보다 코드가 짧아지고 가독성도 나쁘지 않다. 다만 디버그 관점에서 보면 Step by Step으로 추적이 용이한 for-loop가 나을수도 있겠다는 생각이 든다. 속도면에서 for-loop가 빠른 성능을 내기는 하나 전체 프로그램에 크게 영향을 미치는 수준이 아니라면 Stream과 for-loop중 가독성이 좋은 쪽으로 코드를 작성하는게 좋다고 생각한다.
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args){
List<String> names = new ArrayList<>();
names.add("John");
names.add("Joey");
names.add("Sunny");
// 필터링
Stream<String> filteredNames = names.stream().filter((name) -> name.startsWith("J"));
// 출력
filteredNames.forEach((name) -> System.out.println("Hello " + name));
System.out.println(" ======================================================== ");
// 변환
Stream<String> upperCaseNames = names.stream().map((name) -> name.toUpperCase());
// 출력
upperCaseNames.forEach((name) -> System.out.println("upperCase Name = " + name));
}
}
'개발 > Java' 카테고리의 다른 글
다형성이 적용된 Object로 JSON 전환하기 (0) | 2022.09.10 |
---|---|
[Java] Multi Thread와 임계영역 설정 (0) | 2021.09.01 |