int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random() * 5) + 1);
자바에서는 모든 메서드는 클래스에 포함되어야 하므로 클래스도 새로 만들어야 하고, 객체도 생성해야만 비로소 메서드를 호출할 수 있다. 그러나 람다식은 이 모든 과정이 필요없다.
람다식은 값처럼 다룰 수 있어 메서드의 매개변수, 반환값이 될 수 있다. (함수형 프로그래밍)
람다식은 익명 클래스의 객체와 동등하다. (MyFunction은 함수형 인터페이스)
(a, b) -> a > b ? a : b
new MyFunction() {
int max(int a, int b) {
return a > b ? a : b;
}
}
// 함수형 인터페이스
@FunctionalInterface
interface MyFunction {
int max(int a, int b); // 추상 메서드
}
// 1. 함수형 인터페이스를 구현한 익명 객체
MyFunction f = new MyFunction() {
public int max(int a, int b) {
return a > b ? a : b;
}
};
int big = f.max(5, 3); // 익명 객체의 메서드 호출
// 2. 람다식
MyFunction f = (int a, int b) -> a > b ? a : b;
int big = f.max(5, 3) // 익명 객체의 메서드 호출
하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것은 기존의 자바의 규칙들을 어기지 않으면서도 자연스럽다. 그래서 인터페이스를 통해 람다식을 다루기로 결정되었으며, 람다식을 다루기 위한 인터페이스를 함수형 인터페이스라고 부르기로 했다.
함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야 한다는 제약이 있다. 그래야 람다식과 인터페이스의 메서드가 1:1로 연결될 수 있다.
@FunctionalInterface
interface MyFunction {
void run();
}
static void execute(MyFunction function) {
function.run();
}
MyFunction f1 = () -> System.out.println("f1.run()");
execute(f1);
static MyFunction getMyFunction() {
MyFunction function = () -> System.out.println("f3.run()");
return function;
}
java.util.function 패키지에 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 정의되어 있다.
매번 새로운 함수형 인터페이스를 정의하지 말고, 가능하면 이 패키지의 인터페이스를 활용하는 것이 좋다. 그래야 함수형 인터페이스에 정의된 메서드 이름도 통일되고, 재사용성이나 유지보수 측면에서 좋다.
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;
| 함수형 인터페이스 | 메서드 시그니처 | 설명 |
|---|---|---|
Runnable | void run() | 매개변수와 반환값이 모두 없음 |
Supplier<T> | T get() | 매개변수는 없고, 반환값만 있음 |
Consumer<T> | void accept(T t) | 매개변수만 있고, 반환값은 없음 |
Function<T, R> | R apply(T t) | 하나의 매개변수를 받아 결과를 반환 |
Predicate<T> | boolean test(T t) | 하나의 매개변수를 받아 조건을 판별 (boolean 반환) |
BiConsumer<T, U> | void accept(T t, U u) | 두 개의 매개변수를 받아 처리하고 반환값은 없음 |
BiFunction<T, U, R> | R apply(T t, U u) | 두 개의 매개변수를 받아 하나의 결과를 반환 |
BiPredicate<T, U> | boolean test(T t, U u) | 두 개의 매개변수를 받아 조건을 판별 (boolean 반환) |
UnaryOperator<T> | T apply(T t) | Function의 특수 형태. 입력과 출력의 타입이 동일 |
BinaryOperator<T> | T apply(T t1, T t2) | BiFunction의 특수 형태. 두 입력과 결과의 타입이 동일 |
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i % 2 == 0;
Predicate<Integer> notP = p.negate();
System.out.println(notP.and(q.or(r)).test(150)); // true
String str1 = "abc";
String str2 = "abc";
Predicate<String> p2 = Predicate.isEqual(str1);
System.out.println(p2.test(str2)); // true
Function<String, Integer> f = (String s) -> Integer.parseInt(s); // 람다식
Function<String, Integer> f = Integer::parseInt // 메서드 참조
// 생성자의 메서드 참조
// 매개변수 없을 때
Supplier<MyClass> s = () -> new MyClass(); // 람다식
Supplier<MyClass> s = MyClass::new; // 메서드 참조
// 매개변수 1개일 때
Function<Integer, MyClass> f = (i) -> new MyClass(i); // 람다식
Function<Integer, MyClass> f = MyClass::new; // 메서드 참조
// 매개변수 2개일 때
BiFunction<Integer, String, MyClass> bf = (i, s) -> new MyClass(i, s); // 람다식
BiFunction<Integer, String, MyClass> bf = MyClass::new; // 메서드 참조
// 배열 생성
Function<Integer, int[]> f = x -> new int[x]; // 람다식
Function<Integer, int[]> f = int[]::new // 메서드 참조