Java

람다, 함수형 인터페이스, 메서드 참조

레이코딩 2022. 12. 8. 16:13

출처 - 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;