람다식이란 ?
람다식이란 쉽게 말해 메서드를 "하나의 식"으로 표현한 것입니다.
하나의 식으로 표현하여 훨씬 간략하게 표현이 가능하게 되며, 메서드의 이름과 반환값이 없어지므로 "익명함수"라고도 합니다.
int[] arr = new int[5]
Arrays.setAll(arr, (i) -> (int) (Math.random() * 5) + 1);
// (i) - (int) (Math.random() * 5) + 1 람다식을 메서드로 표현
int method() {
return (int) (Math.random() * 5) + 1;
}
위 코드에서는 int배열 arr에 setAll의 두 번째 인자로 채우는 코드를 가지고 있습니다.
(i) -> (int) (Math.random() * 5) + 1 가 람다식이며 이 람다식은 아래의 주석으로 처리된 메서드와 같습니다.
훨씬 간략해졌죠 ?
이렇게 람다식은 오직 람다식 자체만으로도 메서드이 역할을 대신할 수 있으며, 메서드의 매개변수로 전달되어지는 것도 가능하고, 메서드이 결과로 반환될 수도 있습니다.
즉, 람다식으로 메서드를 변수처럼 다루는 것이 가능한 것 입니다.
람다식 작성하기
람다식은 "익명 함수"답게 메서드에서 이름과 반환 타입을 제거하고 매개변수 선언부와 몸통 { } 사이에 " -> "를 추가합니다.
예를 들어서 두 값 중 큰 값을 반환하는 메서드 max를 람다식으로 변환한다면, 아래와 같습니다.
- 반환 값이 있는 메서드의 경우, return문을 생략하고 식으로 대신합니다.
- 그리고 이 식의 끝에 세미콜론 ' ; '을 붙이지 않습니다.
- 람다식에 선언된 매개변수의 타입 또한 생략이 가능합니다.
만약 매개변수가 하나뿐인 경우에는 괄호( )를 생략할 수 있습니다.
단, 매개변수의 타입이 있으면 괄호()를 생략할 수 없습니다.
또한 중괄호{} 안의 문장이 하나일 때는 중괄호를 생략가능 한데, 문장의 끝에 세미콜론' ; '을 붙이지 않는 것에 주의해야합니다.
하지만 중괄호{} 안의 문장이 return문일 경우 중괄호를 생략할 수 없고 문장의 끝에 세미콜론도 생략하지 않습니다.
아래의 람다식 변환 예제들을 보면서 각 상황별 변환법에 대해 이해해봅시다.
메서드를 람다식으로 변환 예제
메서드 | 람다식 |
void printVal(String name, int i) { System.out.println(name + "=" + i); } |
(name, i) -> System.out.println(name + "=" + i) |
int square(int x) { return x * x } |
x -> x * x |
int roll() { return (int) (Math.random() * 6); } |
() -> { return (int) (Math.random() * 6); } |
int sumArr(int[] arr) { int sum = 0; for (int i : arr) { sum += i; } return sum; } |
(int[] arr) -> { int sum = 0; for (int i : arr) { sum += i; } return sum; } |
java.util.function 패키지
일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해 놓은 패지키입니다.
매번 새로운 함수형 인터페이스를 정의하지 않고, 이 패키지의 인터페이스를 활용함으로써 재사용할 수 있고 유지보수 측면에서도 좋습니다.
java.util.function 패키지의 기본적인 주요 함수형 인터페이스
매개변수와 반한값의 유무에 따라 4개의 함수형 인터페이스가 정의되어 있고,
Function의 변형으로 Predicate가 있는데 반환값이 boolean이라는 것만 제외하면 Fuction과 동일합니다.
함수형 인터페이스 | 메서드 | 반환타입 | 설명 |
java.lang.Runnable | run() | void | 매개변수도 없고, 반환 값도 없다. |
Supplier<T> | get() | T | 매개변수는 없고 반환 값만 있다. |
Consumer<T> | accept(T t) | void | Suplier와 반대로 매개변수만 있고, 반환 값이 없다. |
Function<T,R> | apply(T t) | R | 일반적인 함수로, 하나의 매개변수를 받아서 결과를 반환한다. |
Predicate<T> | test(T t) | boolean | 조건식을 표현하는데 사용되며 매개변수는 하나, 반환 타입은 boolean이다. |
- Predicate 사용 예시
Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";
if (isEmptyStr.test(s)) { // if (s.length() == 0); 와 같다.
System.out.println("빈 문자열 입니다");
}
매개변수가 두 개인 함수형 인터페이스
매개변수의 개수가 2개인 함수형 인터페이스는 이름 앞에 접두사 "Bi"가 붙습니다.
함수형 인터페이스 | 메서드 | 반환타입 | 설명 |
BiConsumer<T, U> | accept(T t, U u) | void | 두 개의 매개변수만 있고, 반환 값이 없다. |
BiFunction<T, U, R> | apply(T t, U u) | R | 두 개의 매개변수를 받아서 하나의 결과를 반환한다. |
BiPredicate<T, U> | test(T t, U u) | boolean | 조건식을 표현하는데 사용되며 매개변수는 둘, 반환 타입은 boolean. |
UnaryOperator와 BinaryOperator
Function의 변형으로 매개변수의 타입과 반환타입이 모두 일치한다는 점 외에는 Function과 같습니다.
함수형 인터페이스 | 메서드 | 반환타입 | 설명 |
UnaryOperator(T) | apply(T t) | T | Function의 자손 Function과 달리 매개변수와 결과의 타입이 같다. |
BinaryOperator<T> | apply(T t, T t) | T | BiFunction의 자손 BiFunction과 달리 매개변수와 결과의 타입이 같다. |
컬렉션 프레임웍과 함수형 인터페이스
컬렉션 프레임웍의 인터페이스에 다수의 default 메서드가 추가되었는데,
그 중 함수형 인터페이스에 사용되는 메서드의 목록입니다.
인터페이스 | 메서드 | 반환타입 | 설 명 |
Collection | removeIf(predicate<E> filter) | boolean | 조건에 맞는 요소를 삭제 |
List | replaceAll(UnaryOperator<E> operator) | void | 모든 요소를 변환하여 대체 |
Iterable | forEach(Consumer<T> action) | void | 모든 요소에 작업 action을 수행 |
Map | compute(K key, Bifunction<K,V,V> f) | V | 지정된 키의 값에 작업 f를 수행 |
coputeIfAbsent(K key, Function<K,V> f) | V | 키가 없으면, 작업 f 수행 후 추가 | |
coputeIfPresent(K key, BiFunction<K,V,V> f) | V | 모든 요소에 병합작업 f를 수행 | |
forEach(BiConsumer<K,V> action) | void | 모든 요소에 작업 action을 수행 | |
replaceAll(BiFunction<K,V,V> f) | void | 모든 요소에 치환작업 f를 수행 |
람다식 예제
컬렉션 프레임웍 함수형 인터페이스 메서드 예제
import java.util.*;
class LambdaEx4 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for(int i=0;i<10;i++)
list.add(i);
// list의 모든 요소를 출력
list.forEach(i->System.out.print(i+","));
System.out.println();
// list에서 2 또는 3의 배수를 제거한다.
list.removeIf(x-> x%2==0 || x%3==0);
System.out.println(list);
list.replaceAll(i->i*10); // list의 각 요소에 10을 곱한다.
System.out.println(list);
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "4");
// map의 모든 요소를 {k,v}의 형식으로 출력한다.
map.forEach((k,v)-> System.out.print("{"+k+","+v+"},"));
System.out.println();
}
}
함수형 인터페이스 사용 에제
import java.util.function.*;
import java.util.*;
class LambdaEx5 {
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<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("]");
}
static <T> void makeRandomList(Supplier<T> s, List<T> list) {
for(int i=0;i<10;i++) {
list.add(s.get());
}
}
}
참고자료 : 자바의 정석3
'◼ JAVA' 카테고리의 다른 글
[Java/자바] List와 String(문자열, 배열)을 서로 변환하는 법 (0) | 2022.12.04 |
---|---|
[Java/자바] 정규식(Regular Expression) 사용법 총정리 (2) | 2022.11.28 |
[Java/자바] Enum(열거형)이란? (0) | 2022.11.25 |
[우아한테크코스 백엔드 5기] 프리코스 4주차 후기 (다리 건너기) (0) | 2022.11.23 |
[Java/자바] JUnit5 Assertions 예제 정리 (0) | 2022.11.21 |