[기본 개념] 9 | (1.2) Java.util.funcion 패키지, Function 합성, Predicate 결합, 메서드 참조
1 람다식이란?
2 람다식 작성하기
3 함수형 인터페이스(Functional Interface)
4> java.util.function패키지
5> Function의 합성과 Predicate의 결합
6> 메서드 참조
4. java.util.function패키지
java.util.function패키지에 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 정의해 놓았다. 가능하면 이 패키지의 인터페이스를 사용하는 것이 메서드 이름도 통일되고, 재사용성이나 유지보수 측면에서 좋다.
함수형 인터페이스 | 메서드 | 설명 |
java.lang.Runnable | void run( ) | 매개변수도 없고, 반환값도 없음 |
Supplier<T> | T get( ) → T | 매개변수는 없고, 반환값만 있음 |
Consumer<T> | T → void accept(T t) | 매개변수만 있고, 반환값이 없음 |
Function<T, R> | T → R apply(T t) → R | 일반적인 함수. 하나의 매개변수를 받아 결과를 반환함 |
Predicate<T> | T → boolean test(T t) → boolean | 조건식을 표현하는 데 사용함 매개변수는 하나, 반환타입은 boolean |
매개변수가 두 개인 함수형 인터페이스
매개변수가 2개인 함수형 인터페이스는 접두사 'Bi'가 붙는다.
함수형 인터페이스 | 메서드 | 설명 |
BiConsumer<T, U> | T, U → void accept(T t, U u) | 두 개의 매개변수만 있고, 반환값이 없음 |
BiPredicate<T, U> | T, U → boolean test(T t, U u) → boolean | 조건식을 표현하는 데 사용됨 매개변수는 둘, 반환값은 boolean |
BiFunction<T, U, R> | T, U → R apply(T t, U u) → R | 두 개의 매개변수를 받아서 하나의 결과를 반환 |
UnaryOperator와 BinaryOperator
Function의 또 다른 변형으로 매개변수의 타입과 반환타입이 일치한다.
함수형 인터페이스 | 메서드 | 설명 |
UnaryOperator<T> | T → T apply(T t) → T | Function의 자손, Function과 달리 매개변수와 결과의 타입이 같다. |
BinaryOperator<T> | T, T → T apply(T t, T t) → T | BiFunction의 자손, Bifunction과 달리 매개변수와 결과의 타입이 같다. |
함수형 인터페이스 예제
예제/LamdaEx5.java
import java.util.function.*;
import java.util.*;
public class LamdaEx5 {
public static void main(String[] args) {
Supplier<Integer> s = () -> (int) (Math.random() * 100) + 1;
Consumer<Integer> c = i -> System.out.print(i + ", ");
Predicate<Integer> p = i -> i % 2 == 0;
Function<Integer, Integer> f = i -> i / 10 * 10; // i의 일의 자리를 없앤다.
List<Integer> list = new ArrayList<>();
makeRandomList(s, list);
System.out.println(list);
printEvenNum(p, c, list);
List<Integer> newList = doSomething(f, list);
System.out.println(newList);
}
static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
List<T> newList = new ArrayList<>(list.size());
for (T i : list) {
newList.add(f.apply(i));
}
return newList;
}
static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
System.out.print("[");
for (T i : list) {
if (p.test(i)) c.accept(i);
}
System.out.println("]");
}
static <T> void makeRandomList(Supplier<T> s, List<T> list) {
for (int i = 0; i < 10; i++) list.add(s.get());
}
}
실행결과
[41, 45, 100, 4, 72, 77, 29, 10, 83, 89]
[100, 4, 72, 10, ]
[40, 40, 100, 0, 70, 70, 20, 10, 80, 80]
5. Function의 합성과 Predicate의 결합
Function의 합성
두 람다식을 합성해서 새로운 람다식을 만들 수 있다.
함수 f, g가 있을 때, f.addThen(g)는 함수 f 먼저 적용하고 함수 g를 적용하지만, f.compose(g)는 반대로 함수 g 먼저 적용하고 함수 f를 적용한다.
예를 들어, 문자열을 숫자로 변환하는 함수 f, 숫자를 이진 문자열로 변환하는 함수 g로 andThen( )으로 합성하여 새로운 함수 h를 만들어낸다.
Function<String, Integer> f = (s) -> Integer.parseInt(s, 16) ;
Function<Integer, String> g = (i) -> Integer.toBinaryString(i) ;
Function<String, String> h = f.andThen(g) ;
함수 h의 지네릭 타입이 <String, String>이다. 예를 들어 "FF"를 입력하면, 결과로 "11111111"를 얻는다.
System.out.println(h.apply("FF")) ; // "FF" -> 255 -> "11111111"
compose( )를 사용해서 반대 순서로 합성해보자.
Function<Integer, String> g = (i) -> Integer.toBinaryString(i) ;
Function<String, Integer> f = (s) -> Integer.parseInt(s, 16) ;
Function<Integer, Integer> h = f.compose(g) ;
함수 h의 지네릭 타입이 <Integer, Integer>이다. 예를 들어 2를 입력하면, 결과로 16을 얻는다.
System.out.println(h.apply(2)) ; // 2 -> "10" -> 16
indentity( )라는 항등함수가 필요할 때 사용한다. 이 함수는 람다식으로 'x -> x'이다.
Function<String, String> f = Function.identity( ) ; // x -> x ; 로도 사용할 수 있다.
System.out.println(f.apply("AAA")) ; // "AAA"가 그대로 출력
잘 사용되지는 않지만 map( )으로 변환작업할 때, 변환없이 그대로 처리할 때 사용한다.
Predicate의 결합
여러 Predicate를 and( ), or( ), negate( )로 연결해서 하나의 새로운 Predicate로 결합한다.
Predicate<Integer> p = i -> i < 100 ;
Predicate<Integer> q = i -> i < 200 ;
Predicate<Integer> r = i -> i % 2 == 0 ;
Predicate<Integer> notP = p.negate( ) ;
// 100 <= i && (i < 200 || i % 2 == 0)
Predicate<Integer> all = notP.and(q.or(r)) ; // notP.and(i -> i < 200).or(i -> i % 2 == 0) ; 람다식도 가능
System.out.println(all.test(150)) ; // true
Predicate의 끝에 negate( )를 붙이면 조건식 전체가 부정이 된다.
그리고 static메서드인 isEqual( )은 두 대상을 비교하는 Predicate를 만들 때 사용한다. isEquals( )의 매개변수로 비교대상 하나를 지정하고, 다른 비교대상은 test( )의 매개변수로 지정한다.
boolean result = Predicate.isEquals(str1).test(str2) ; // str1과 str2가 같은지 비교
6. 메서드 참조
람다식이 하나의 메서드만 호출하는 경우에, 참조변수의 타입으로 알 수 있으므로 람다식의 매개변수를 제거해 메서드 참조로 변경할 수 있다.
BiFunction<String, String, Boolean> f = (s1, s2) -> s1.equals(s2) ;
BiFunction<String, String, Boolean> f = String :: equals ; // 메서드 참조
종류 | 람다 | 메서드 참조 |
static메서드 참조 | (x) -> ClassName.method(x) | ClassName :: method |
인스턴스메서드 참조 | (obj, x) -> obj.mehtod(x) | ClassName :: method |
특정 객체 인스턴스메서드 참조 | (x) -> obj.method(x) | obj :: method |
하나의 메서드만 호출하는 람다식은
'클래스이름 :: 메서드 이름' 또는 '참조변수 :: 메서드 이름'으로 바꿀 수 있다.
생성자의 메서드 참조
생성자를 호출하는 람다식도 메서드 참조로 변환할 수 있다.
Supplier<MyClass> s = ( ) -> new MyClass( ) ; // 람다식
Supplier<MyClass> s = MyClass :: new ; // 메서드 참조
매개변수가 있는 생성자라면, 매개변수의 개수에 맞는 함수형 인터페이스를 사용하면 된다.
배열을 생성할 때는 아래와 같이하면 된다.
Function<Integer, int[ ]> f = x -> new int[x] ; // 람다식
Function<Integer, int[ ]> f2 = int[ ] :: new ; // 메서드 참조
메서드 참조는 람다식을 static변수처럼 다룰 수 있게 해 준다.
출처 | Java의 정석 (남궁 성)
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[기본 개념] 9 | (2.2) 스트림 중간 연산 (0) | 2022.02.10 |
---|---|
[기본 개념] 9 | (2.1) 스트림 (0) | 2022.01.26 |
[기본 개념] 9 | (1.1) 람다식, 람다식 인터페이스 (0) | 2022.01.25 |
[기본 개념] 8 | (1.6) 스레드 동기화(Lock, Condition, Volatile, Fork&Join 프레임웍 (0) | 2022.01.24 |
[기본 개념] 8 | (1.5) 스레드 동기화(Synchronized, wait( ), notify( )) (0) | 2022.01.23 |