람다, 함수형 인터페이스, 메서드 참조
출처 - Java의 정석 기초편 (남궁 성)
1. 람다식(Lambda Expression)
- 함수(≒메서드)→식
- 익명 함수(anonymous function)
- 메서드: 클래스에 포함, 객체 생성시 메서드 호출 가능(메서드는 클래스에 종속적)
- 람다식: 메서드의 매개변수, 반환값→메서드를 변수처럼 다룸
2. 람다식 작성
- 메서드 이름, 반환타입 제거하고 '->' 추가
- 반환값이 있으면 식이나 값만 적고 return문 생략 가능(끝에 ';' 안 붙임)
- 매개변수 타입이 추론 가능하면 대부분의 경우 생략 가능
- 매개변수가 하나면 괄호() 생략 가능(타입이 없을 때만)
- 문장이 하나면 블럭{} 생략 가능(끝에 ';' 안 붙임)
3. 람다식은 익명 함수? 익명 객체!
- 람다식=익명 객체
- 객체 다루려면 참조변수 필요(Object 타입 불가)
- 함수현 인터페이스 선언→함수형 인터페이스 타입 참조변수로 람다식 참조
new Object() {
int max(int a, int b) {
return a > b ? a : b;
}
}
// 객체의 선언과 생성 동시에
(a, b) -> a > b ? a : b
// 람다식 = 익명 클래스의 익명 개체
3. 함수형 인터페이스(Functional Interface)
- 단 하나의 추상 메서드만 선언된 인터페이스
- 함수형 인터페이스 타입 참조변수로 람다식 참조
- 단, 함수형 인터페이스 메서드와 람다식 매개변수 개수, 반환타입 일치
@FunctionalInterface
interface MyFunction {
public abstract int max(int a, int b);
}
MyFunction f = new MyFunction() {
public int max(int a, int b) {
return a > b ? a : b;
}
};
MyFunction f = (int a, int b) -> a > b ? a : b; // 익명 객체를 람다식으로 대체
4. 함수형 인터페이스 타입
- 메서드 매개변수: 이 메서드의 매개변수로 람다식 받음
- 메서드 반환타입: 이 메서드는 람다식 참조변수 또는 람다식 직접 반환
@FunctionalInterface
interface MyFunction {
void myMethod();
}
void aMethod(MyFunction f) {
f.myMethod();
}
MyFunction f = () -> system.out.println("myMethod()");
aMethod(f); // 한 줄로. aMethod(() -> system.out.println("myMethod()"));
MyFunction myMethod() {
MyFunction f = ()->{};
return f; // 한 줄로. return ()->{};
}
5. java.util.function패키지
- 매개변수와 반환값 유무에 따라 함수형 인터페이스 제공→재사용성 용이
- 매개변수 2개: 접두사 Bi
- 매개변수 타입과 반환타입 일치: UnaryOperator, Binary Operator
java.lang.Runnable // void run() - 입출력 없음
Supplier<T> // T get() - 출력만 있음(공급자)
Consumer<T> // void accept(T t) - 입력만 있음(소비자)
Function<T,R> // R apply(T t) - 입출력 있음(함수/일반적)
Predicate<T> // boolean test(T t) - 조건식
BiConsumer<T,U> // void accept(T t, U u)
BiPredicate<T,U> // boolean test(T t, U u)
BiFunction<T,U,R> // R apply(T t, U u)
// BiSupplier는 없음
// 2개 이상 직접 정의
@FunctionalInterface
interface TriFunction<T,U,V,R> { // 매개변수 3개
R apply(T t, U u, V v);
}
// Function, BiFunctio 자손
UnaryOperator<T> // T apply(T t)
BinaryOperator<T> // T apply(T t, T t)
6. Predicate의 결합
- and(), or(), negate()로 두 Predicate를 하나로 결합(default메서드)
- 등가비교: isEqual(), test() 사용(static메서드)
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> notP = p.negate();
Predicate<Integer> all = notP.and(i -> i < 200).or(i -> i%2 == 0); // 람다식 대입도 가능
Predicate<String> p = Predicate.isEqual(str1);
boolean result = p.test(str2);
boolean result = Predicate.isEqual(str1).test(str2); // 한 줄로
7. 컬렉션 프레임웍과 함수형 인터페이스
// Collection
boolean removeIf(Predicate<E> filter) // 조건에 맞는 요소 삭제
// List
void replaceAll(UnaryOperator<E> operator) // 모든 요소 변환 대체
// Iterable
void forEach(Consumer<T> action) // 모든 요소 작업 수행
// Map
V compute(K key, BiFunction<K,V,V f) // 키값에 작업 f 수행
V computeIfAbsent(K key, Function<K,V> f) // 키가 없으면
V computeIfPresent(K key, BiFunction<K,V,V> f) // 키 있을 때
V merge (K key, V value, BiFunction<V,V,V> f) // 모든 요소 병합작업 f 수행
void forEach(BiConsumer<K,V> action)
void replaceAll(BiFunction<K,V,V> f)
8. 메서드 참조
- 하나의 메서드만 호출하는 람다식→더 간단히
- 클래스 이름::메서드 이름
Function<String, Integer> f = (String s) -> Integer.parseInt(s); // 람다식
Function<String, Integer> f = Integer::parseInt; // 메서드 참조
9. 생성자의 메서드 참조
- 매개변수 없는 경우, 매개변수 있는 경우, 배열
Supplier<MyClass> s = () -> new Myclass();
Supplier<MyClass> s = MyClass::new;
Function<Integer, MyClass> f = (i) -> new MyClass(i);
Function<Integer, MyClass> f = MyClass::new;
BiFunction<Integer, String, MyClass> bf = (i, s) -> new MyClass(i, s);
BiFunction<Integer, String, MyClass> bf2 = MyClass::new;
Function<Integer, int[]> f = x -> new int[x]; // 배열길이에 해당하는 배열
Function<Integer, int[]> f2 = int[]::new; // 배열타입[]::new;