개발공부/JAVA
Java(자바) - Collection&람다식2 : 함수형 인터페이스의 활용
HyunJng
2022. 8. 30. 18:56
Java.util.function 패키지
람다식을 사용할 때 함수형 인터페이스를 어차피 익명으로 사용하기 때문에 매개변수와 return값만 중요하므로 일반적으로 자주 쓰이는 형식의 메서드를 제네릭 타입의 함수형 인터페이스로 미리 구현해두었다.
매개변수와 반환값에 주목하며 살펴보자.
매개변수가 하나인 함수형 인터페이스
함수형 인터페이스 | 메서드 | 설명 |
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> | boolean test(T t) | 매개변수 O, 반환타입 boolean |
매개변수가 두개인 함수형 인터페이스
접두사 Bi가 붙는다.
함수형 인터페이스 | 메서드 | 설명 |
BiConsumer<T, U> | void accept(T t, U u) | 매개변수 O, 반환값 X |
BiFunction<T, U, R> | R apply(T t, U u) | 매개변수 O, 반환값 O |
BiPredicate<T, U> | boolean test(T t, U u) | 매개변수 O, 반환타입 boolean |
Function의 변형
매개변수 타입과 반환 타입이 동일하다는 점을 제외하고 Function과 동일하다.
함수형 인터페이스 | 메서드 | 설명 |
UnaryOperator<T> | T apply(T t) | Function의 자손 Function에서 매개변수와 반환 타입이 동일할 때 사용한다. |
BinaryOperator<T> | T apply(T t, T t) | BiFunction의 자손 BiFunction에서 매개변수와 반환 타입이 동일할 때 사용한다. |
매개변수가 세 개 이상은 직접 만들어서 써야한다.
함수형인터페이스 사용 예제
import java.util.function.*;
import java.util.*;
public class LamdaEx5 {
public static void main(String[] args) {
Supplier<Integer> s = () -> (int)(Math.random() * 100) + 1;
Predicate<Integer> p = i -> i%2 == 0;
Consumer<Integer> c = i -> System.out.print(i + ", ");
Function<Integer, Integer> f = i -> i/10 * 10;
List<Integer> list = new ArrayList<Integer>();
// 랜덤한 값을 10개 넣어줌
makeRandomList(list, s);
System.out.println(list);
//짝수 조건식만 출력
printEvenNum(p, c, list);
// 기본 list에서 일의 자리 수는 버린 새로운 list 생성
List<Integer> newList = doSomething(list, f);
System.out.println(newList);
}
// 리스트를 받고, 특정 행위를 한 뒤 새 리스트를 반납하는 함수
static <T> List<T> doSomething(List<T> list, Function<T, T> f) {
List<T> newList = new ArrayList<T>(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("]");
}
// 받은 리스트 안에 특정한 조건의 값을 10개 넣어주는 메서드
static <T> void makeRandomList(List<T> list, Supplier<T> s) { // 매개변수 X, return O
for (int i = 0; i < 10; i++)
list.add(s.get());
}
}
랜덤이라 돌릴때마다 숫자는 바뀐다.
컬렉션 프레임워크와 함수형 인터페이스
컬렉션 프레임 웍의 인터페이스에 다수의 디폴트 메서드가 추가되었는데, 그 중의 일부는 함수형 인터페이스를 매개변수로 받고 있기 때문에 람다식을 안에 넣을 수 있다.
인터페이스 | 메서드 | 설명 |
Collections | boolean removeIf(Predicate<E> filter) | filter식이 true인 요소를 삭제 |
List | void replaceAll(UnaryOperator<E> operator) | 모든 요소에 operation 작업 후 다시 넣어줌 |
Iterable | void forEach(Consumer<T> action) | 모든 요소에 작업 action을 수행 |
Map | V compute(K key, BiFunction<K, V, V> f) | 지정된 키의 값(value)에 작업 f를 수행 |
V computeIfAbsent(K key, Function<K, V> f) | 키가 없으면, 작업 f를 수행 후 추가 | |
V computeIfPresent(K key, BiFunctio<K, V, V> f) | 키가 있으면, 작업 f를 수행 | |
V merge(K key, V value, BiFunction<V, V, V> fv) | 모든 요소에 병합작업 f를 수행 | |
void forEach(BiConsumer<K, V> action) | 모든 요소에 작업 action을 수행 | |
void replaceAll(BiFunctio<K, V, V> f) | 모든 요소에 치환작업 f를 수행 |
* 맵의 compute는 value를 변환하는 일, merge는 병합하는 일.
* list / set의 경우 람다식의 매개변수에는 원소값이 들어가고
map의 경우는 람다식에 key나 value가 들어간다.
Collection에서의 람다식 사용 예제
import java.util.*;
public class LamdaEx4 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i = 10; i > 0; i--)
list.add(i);
// 모든 요소를 출력 ( i에는 list에 저장된 값을 하나씩 대입)
System.out.print("list.forEach의 결과: ");
list.forEach(i -> System.out.print(i + ","));//consumer : 매개변수 O, return X
// list의 2또는 3의 배수를 제거
list.removeIf(x-> x%2 == 0 || x%3 == 0);
System.out.print("\nremoveIf의 결과: "+list);
// list의 각 요소에 10을 곱함
list.replaceAll(i -> i * 10);
// list.forEach(i -> i * 10); // 안되는 이유: forEach에는 다시 넣어주는 기능은 없기 떄문.
System.out.print("\nreplaceAll의 결과: "+list);
Map<String, String> map = new HashMap<String, String>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "4");
//map의 모든 요소를 {k, v} 형식으로 출력
System.out.print("\nmap.forEach의 결과: ");
map.forEach((i, j) -> System.out.print("{" + i + ", " + j + "}, "));
}
}
+) 기본형을 사용하는 함수형 인터페이스
위의 함수형 인터페이스는 모두 반환값과 매개변수 타입이 제너릭 타입이었는데, 기본형 타입의 값을 처리할 때도 Wrapper클래스를 사용해왔다. 그러나 기본형은 기본형으로 처리할 때 더 효율적이므로 기본형을 사용하는 함수형 인터페이스도 제공한다.
함수형 인터페이스 | 메서드 | 설명 |
DoubleToIntFunction | int applyAsInt(double d) | AToBFunction은 입력이 A타입, 출력이 B타입 |
ToIntFunction<T> | int applyAsInt(T value) | ToAFunction은 입력은 제네릭타입, 출력은 A타입 |
IntFunction<R> | R apply(T t, U u) | AFunction은 입력은 A타입, 출력은 제네릭타입 |
ObjIntConsumer<T> | void accept(T t, U u) | ObjAFunction은 입력이 T, A타입이고 출력은 없다. |
그냥.. 이런것도 있다 정도만 알아두자.