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 |