본문 바로가기

Java

Java 람다(Lambda)

 

Java는 객체지향프로그래밍(OOP) 언어이지만 JDK1.8부터 함수형 언어의 기능이 더해짐

함수형 언어: Haskell, Evlang, Scala 등 빅데이터 처리를 위해 많이 사용되고 있음

 

 

1.  람다식(Lambda Expression)

- 메서드를 간단한 식(expression)으로 표현한 것

- 메서드를 람다식으로 표현 시 이름과 반환값이 없어지므로 익명 함수(anonymous function)라고도 함

 

 

2. 람다식 작성하기

 

1) 반환타입, 메서드명 제거

// 메서드
int max(int a, int b) {
}

// 람다식
(int a, int b) { }
// 1. 반환타입, 메서드명 제거

 

 

2) 매개변수 선언부와 몸통'{}' 사이에 화살표 '->' 추가

// 메서드
int max(int a, int b) {
	return a > b ? a : b;
}

// 람다식
(int a, int b) -> {return a > b ? a : b;}
// 1. 반환타입, 메서드명 제거  2. 블록 앞 '->' 추가

 

 

3) 매개변수 생략

- 매개변수가 하나이고, 매개변수 타입이 없는 경우 괄호'()' 생략 가능

// 메서드
int default(a) {
	return a * a
}

// 람다식
a -> { }
// 1. 반환타입, 메서드명 제거  2. 블록 앞 '->' 추가  3. 매개변수가 하나이고 매개변수 타입이 없는 경우 괄호 생략

 

- 매개변수의 타입이 추론 가능하면 생략 가능

// 메서드
int max(int a, int b) {
	return a > b ? a : b;
}

// 람다식
(a, b) -> {return a > b ? a : b;}
 // 1. 반환타입, 메서드명 제거  2. 블록 앞 '->' 추가  3. 매개변수의 타입이 추론 가능하여 생략

 

 

4) 블록'{}' 안

- 반환값이 있는 메서드의 경우, return문 대신 식(expression)으로 대신 할 수 있음(끝에 세미콜론';'  붙이지 않음) 

// 메서드
int max(int a, int b) {
	return a > b ? a : b;
}

// 람다식
(a, b) -> {a > b ? a : b}
 // 1. 반환타입, 메서드명 제거  2. 블록 앞 '->' 추가  3. 매개변수의 타입이 추론 가능하여 생략 4. 반환값이 있는 메서드의 경우 리턴문 대신 식으로 대신함

 

- 문장이 하나 뿐일 경우 괄호'{}' 생략 가능(끝에 세미콜론';'  붙이지 않음) 

// 메서드
int max(int a, int b) {
	System.out.println(a * b);
}

// 람다식
(a, b) -> System.out.println(a * b)
// 1. 반환타입, 메서드명 제거 2. 매개변수의 타입이 추론 가능하여 생략 3. 블록 앞 '->' 추가 4. 문장이 하나 뿐일 경우 괄호'{}' 생략

 

- 괄호'{}' 안 문장이 return문인 경우 괄호'{}' 생략 불가

- return문의 식이나 값만 적고 return문 생략 가능(끝에 세미콜론';'  붙이지 않음) 

// 메서드
int max(int a, int b) {
	return a > b ? a : b;
}

// 람다식 1
(a, b) -> {return a > b ? a : b;}
// 1. 반환타입, 메서드명 제거 2. 매개변수의 타입이 추론 가능하여 생략 3. 블록 앞 '->' 추가 4. 문장이 return문인 경우 괄호'{}' 생략 불가

// 람다식 2
(a, b) -> a > b ? a : b
// 1. 반환타입, 메서드명 제거 2. 매개변수의 타입이 추론 가능하여 생략 3. 블록 앞 '->' 추가 4. return문의 식이나 값만 적고 return문과 괄호'{}' 생략

 

 

3.  함수형 인터페이스(Functional Interface)

- 단 하나의 추상 메서드만 선언된 인터페이스

 

1) 참조변수

- 람다식은 익명 클래스의 객체와 동등함, 객체를 다루기 위해서는 참조 변수가 필요함

// 익명 클래스 선언, 객체 생성
Object obj = new Object() {
                    public int max(int a, int b) {
                    	return a > b ? a : b;
                    }
		};


// Error. Object에 max() 메서드가 없기 때문에
int value = obj.max(3, 5);

 

- 함수형 인터페이스 타입의 참조 변수로 람다식을 참조할 수 있음(함수형 인터페이스의 메서드와 람다식의 매개변수 개수, 반환타입이 일치해야함)

// 함수형 인터페이스
@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;
                    }
		};


// OK, MyFunction에 max()메서드 존재함
int value = f.max(3, 5);

 

 

2) 매개변수

// 함수형 인터페이스
@FunctionalInterface
interface MyFunction {
	void myMethod();
}


// 함수형 인터페이스 타입의 매개변수
void aMethod(MyFunction f) {
	f.myMethod(); // MyFunction에 정의된 myMethod()호출
}


// 방법1. 람다식을 참조하는 참조변수 f를 매개변수로 지정
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);

// 방법2. 참조변수 없이 람다식을 직접 매개변수로 지정
aMethod(() -> System.out.println("myMethod()"));

 

 

3) 반환타입

- 메서드의 반환타입이 함수형 인터페이스 타입인 경우, 함수형 인테페이스의 추상 메서드와 동등한 람다식을 가리키는 참조변수를 반환하거나 람다식을 직접 반환함

// 함수형 인터페이스
@FunctionalInterface
interface MyFunction {
	void myMethod();
}


// 1. 추상 메서드와 동등한 람다식을 가리키는 참조변수 f 반환
MyFunction myMethod() { // 메서드의 반환타입이 함수형 인터페이스 타입
    MyFunction f = () -> {};
    return f;
}


// 2. 추상 메서드와 동등한 람다식 직접 반환
MyFunction myMethod() { // 메서드의 반환타입이 함수형 인터페이스 타입
    return () -> {}

 

 

4.  java.util.function 패키지

- 자주 사용되는 다양한 함수형 인터페이스 제공

 

1) 함수형 인터페이스와 메서드

- 매개변수가 없거나 1개인 함수형 인터페이스

함수형 인터페이스
메서드
설명 매개변수 반환값
java.lang.Runnable void run() 매개변수 없음, 반환값 없음 X X
Supplier<T> T get() 매개변수 없음, 반환값 있음 X O
Consumer<T> void accept(T t) 매개변수 있음, 반환값 없음 O X
Function<T,R> R apply(T t) 일반적인 함수로 하나의 매개변수를 받아서 결과 반환 O O
Predicate<T> boolan test(T t) 조건식 표현에 사용함, 매개변수 1개, 반환타입 boolean O O

 

- 매개변수가 2개인 함수형 인터페이스

함수형 인터페이스
메서드
설명 매개변수 반환값
BiConsumer(T,U) void accept(T t, U u) 매개변수 2개, 반환값 없음 O X
BiPredicate(T,U) boolean test(T t, U u) 조건식 표현에 사용, 매개변수 2개, 반환타입 boolean O O
BiFunction(T,U,R) R apply(T t, U u) 매개변수 2개, 반환값 1개 O O

 

- 매개변수가 3개 이상인 경우에는 직접 만들어서 사용

- 매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스

함수형 인터페이스
메서드
설명
UnaryOperator<T> T apply(T t) Function의 자손, 매개변수와 결과의 타입이 같음
BinaryOperator<T> T apply(T t, T, t) BiFunction의 자손, 매개변수와 결과의 타입이 같음

 

 

2) Predicate의 결합

- and(), or(), negate()와 같은 논리 연산자로 두 Predicate를 하나로 결합 👉🏻 default 메서드

// p, q, r 각각의 Predicate
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i%2 == 0;


// 논리 연산자를 이용해 두 Predicate를 하나로 결합
Predicate<Integer> notP = p.negate(); // i >= 100
Predicate<Integer> all = notP.and(q.or(r)); // 100 <= i &&(i < 200 || i%2==0)
Predicate<Integer> all2 = notP.and(q).or(r); // 100 <= i && i < 200 || i%2==0

 

- 등가비교를 위한 Predicate 작성에는 isEqual()를 사용 👉🏻 static 메서드

// str1과 str2가 같은지 비교
Predicate<String> p = Predicate.isEqual(str1); // isEqual()의 매개변수로 비교대상 str1 지정
boolean result = p.test(str2); // 또 다른 비교대상은 test()의 매개변수로 지정   

// 위 두 문장을 합쳐서 표현
boolean result = Predicate.isEqual(str1).test(str2);

 

 

5. 컬렉션 프레임워크와 함수형 인터페이스

- 함수형 인터페이스를 사용하는 컬렉션 프레임워크의 메서드

인터페이스 메서드 설명
Collection boolean removeIf(Predicate<E> filter) 조건에 맞는 요소 삭제
List void replaceAll(UnaryOperator<E> operator) 모든 요소를 변환하여 대체
Iterable void forEach(Consumer<T> action) 모든 요소에 작업 action 수행
Map V compute(K key, BiFunction<K,V,V> f) 지정된 키의 값에 작업 f 수행
V computeIfAbsent(K key, Function<K,V> f) 키가 없는 경우, 작업 f 수행 후 추가
V computeIfPresent(K key, BiFunction(K,V,V) f) 지정된 키가 있는 경우, 작업 f 수행
V merge(K key, V value, BiFunction<V,V,V> f) 모든 요소에 병합 작업 f 수행
void forEach(BiConsumer<K,V> action) 모든 요소에 작업 action 수행
void replaeAll(BiFunction<K,V,V> f) 모든 요소에 치환 작업 f 수행

 

 

6. 메서드 참조(method reference)

- 하나의 메서드를 호출하는 람다식은 '메서드 참조' 로 더 간단히 표현 할 수 있음

종류 람다식 메서드 참조
static 메서드 참조 (x) -> ClassName.method(x) ClassName::method
인스턴스 메서드 참조 (obj, x) -> obj.method(x) ClassName::method
특정 객체 인스턴스 메서드 참조 (x) -> obj.method(x) obj::method

 

1) 메서드 참조

- 클래스명::메서드명 또는 참조변수::메서드명으로 표현

 

2) 생성자와 메서드 참조

// 람다식
Supplier<MyClass> s = () -> new MyClass();

// 메서드 참조
Supplier<MyClass> s = MyClass::new;


// 람다식
Function<Integer, MyClass> s = (i) -> MyClass(i);

// 메서드 참조
Function<Integer, MyClass> s = MyClass::new;

 

 

3) 배열과 메서드 참조

// 람다식
Function<Integer, int[]> f = x -> new int[x]; 

// 메서드 참조
Function<Integer, int[]> f2 = int[]::new;

 

'Java' 카테고리의 다른 글

Java Optional<T>  (0) 2023.01.20
Java 스트림(stream)  (0) 2023.01.20
Java 스레드(thread)  (0) 2023.01.17
Java 애너테이션(annotation)이란?  (0) 2023.01.17
Java 열거형(enum)  (0) 2023.01.17