람다식은 자바 8에서 도입된 익명 함수로, 메서드를 간결하게 표현하는 방식이다. 메서드 이름과 반환형, 접근 제어자 등을 쓰지 않고도, 함수를 간단하게 작성할 수 있어 코드 가독성이 높아진다.
람다식은 기본적으로 다음과 같은 형태를 가진다:
(매개변수) -> { 실행할 코드 }
이것을 더 구체적으로 설명해 보면:
기본적인 람다식 예시로, 두 숫자를 더하는 메서드를 람다식으로 표현할 수 있다.
(int a, int b) -> { return a + b; }
기존 방식과 비교해 보자.
기존 방식:
public interface Adder {
int add(int a, int b);
}
Adder adder = new Adder() {
@Override
public int add(int a, int b) {
return a + b;
}
};
System.out.println(adder.add(5, 3)); // 출력: 8
이 코드는 인터페이스를 구현한 익명 클래스를 생성해서 두 수를 더하는 코드다. 이 코드를 람다식으로 바꾸면 다음과 같다.
람다식 사용:
Adder adder = (a, b) -> a + b;
System.out.println(adder.add(5, 3)); // 출력: 8
람다식은 다음과 같은 규칙을 가지고 있어 더 간결하게 작성할 수 있다:
매개변수가 하나일 때는 괄호 생략 가능
Consumer<String> printer = s -> System.out.println(s);
코드가 한 줄일 때는 중괄호와 return
생략 가능
return
을 생략할 수 있다.(a, b) -> a + b
람다식을 사용하려면 함수형 인터페이스가 필요하다. 함수형 인터페이스는 단 하나의 추상 메서드만 가지고 있는 인터페이스다. 자바에서 제공하는 몇 가지 함수형 인터페이스를 알아보자:
Runnable
: 인자가 없고 반환값도 없는 함수형 인터페이스Comparator<T>
: 두 개의 값을 비교하는 함수형 인터페이스Predicate<T>
: 조건을 테스트해 true
또는 false
를 반환하는 함수형 인터페이스1) Runnable
인터페이스 사용 예시:
기본적으로 자바에서 Runnable
은 인자가 없고, 반환값도 없는 함수형 인터페이스이다. 람다식을 사용하면 더욱 간결해진다.
Runnable task = () -> System.out.println("Hello, Lambda!");
task.run();
2) Comparator
인터페이스 사용 예시:
Comparator
는 두 객체를 비교하는 함수형 인터페이스다. 람다식을 사용하여 숫자를 비교하는 방법은 다음과 같다.
Comparator<Integer> comparator = (a, b) -> a - b;
int result = comparator.compare(3, 2);
System.out.println(result); // 출력: 1 (3이 2보다 큼)
3) Predicate
인터페이스 사용 예시:
Predicate
는 특정 조건을 검사하고, true
또는 false
를 반환하는 함수형 인터페이스다.
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // 출력: true
람다식과 함께 자바 8에서 도입된 또 다른 중요한 기능은 메서드 참조이다. 메서드 참조는 람다식에서 사용되는 메서드 호출을 간결하게 표현하기 위해 사용된다. 메서드 참조는 ::
기호를 사용하여 작성된다.
메서드 참조는 아래와 같은 방식으로 표현된다:
클래스명::메서드명
인스턴스명::메서드명
클래스명::new
정적 메서드를 참조하는 방식이다. 예를 들어 String.valueOf()
메서드를 메서드 참조로 표현하면 아래와 같다.
Function<Integer, String> func = String::valueOf;
System.out.println(func.apply(123)); // 출력: "123"
인스턴스의 메서드를 참조하는 방식이다. 다음은 System.out.println()
메서드를 메서드 참조로 표현한 예시다.
Consumer<String> printer = System.out::println;
printer.accept("Hello, Method Reference!"); // 출력: Hello, Method Reference!
객체의 생성자를 참조하는 방식이다. 아래는 ArrayList
의 생성자를 메서드 참조로 표현한 예시다.
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get();
람다식과 메서드 참조는 매우 비슷하지만, 메서드 참조는 더 간결하게 표현할 수 있다. 아래는 람다식과 메서드 참조를 비교한 예시이다.
람다식 사용:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 람다식으로 대문자로 변환
names.stream()
.map(name -> name.toUpperCase())
.forEach(name -> System.out::println);
메서드 참조 사용:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 메서드 참조로 대문자로 변환
names.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
메서드 참조는 단순히 메서드를 호출하는 람다식의 경우 더 간결하게 사용할 수 있다.
메서드 참조는 람다식을 간결하게 표현하는 방법이다. 람다식에서 특정 메서드를 호출하는 경우, 메서드 참조를 통해 해당 메서드를 바로 참조할 수 있다. 메서드 참조는 자바 8에서 도입되었으며, ::
연산자를 사용한다.
메서드 참조는 다음과 같이 4가지 형태로 나뉜다:
정적 메서드 참조: 클래스의 정적 메서드를 참조할 때 사용한다.
클래스명::메서드명
String::valueOf
인스턴스 메서드 참조: 특정 객체의 인스턴스 메서드를 참조할 때 사용한다.
인스턴스명::메서드명
System.out::println
임의 객체의 인스턴스 메서드 참조: 특정 클래스의 임의의 객체의 인스턴스 메서드를 참조할 때 사용한다.
클래스명::메서드명
String::toUpperCase
생성자 참조: 객체 생성자를 참조할 때 사용한다.
클래스명::new
ArrayList::new
람다식과 메서드 참조는 서로 대체할 수 있는 개념이다. 메서드 참조는 더 간결하게 메서드를 호출할 수 있게 해준다.
람다식 사용:
Function<Integer, String> func = (i) -> String.valueOf(i);
메서드 참조 사용:
Function<Integer, String> func = String::valueOf;
두 코드는 동일하게 작동하지만, 메서드 참조는 더 간결한 표현이다.
람다식 사용:
Consumer<String> printer = (str) -> System.out.println(str);
메서드 참조 사용:
Consumer<String> printer = System.out::println;
이 코드 역시 메서드 참조가 훨씬 간단하게 표현된다.
람다식 사용:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = numbers.stream()
.map(n -> String.valueOf(n))
.collect(Collectors.toList());
System.out.println(strings); // 출력: [1, 2, 3, 4, 5]
메서드 참조 사용:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = numbers.stream()
.map(String::valueOf)
.collect(Collectors.toList());
System.out.println(strings); // 출력: [1, 2, 3, 4, 5]
람다식 사용:
List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
System.out.println(upperNames); // 출력: [ALICE, BOB, CHARLIE]
메서드 참조 사용:
List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperNames); // 출력: [ALICE, BOB, CHARLIE]
::
연산자를 사용하여 클래스나 인스턴스의 메서드를 참조한다.람다식과 메서드 참조는 자바 8에서 도입된 중요한 기능으로, 코드 가독성과 간결함을 높일 수 있다.