대부분의 메서드는 타입이 비슷하다. 매개변수가 없거나 한두개, 반환 값은 없거나 한개, 제네릭 메서드로 정의하면 매개변수나 반환 타입도 달라도 된다. 그래서 java.util.function패키지에 일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해 두었다.
함수형 인터페이스를 필요할 때 마다 만들어 사용하지말고, function패키지 안에 있는것을 사용하도록 하자.
이렇게 해야 메서드 이름도 통일되고, 재사용성이나 유지보수 측면에서도 매우 좋다.
매개변수와 반환값의 유무에 따라 자주사용하는 4개의 함수형 인터페이스가 정의돼 있으며, Function의 변형으로Predicate가 있는데, 조건식을 표현할때 사용한다.
※ T = Type, R = Return Type
Predicate는 Function의 변형으로, 반환타입이 boolean이라는 것만 다르다. Predicate는 조건식을 람다식으로 표현하는데 사용된다.
Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";
if(isEmptyStr.test(s)) // if (s.length() == 0)
System.out.println("This is an empty String.");
3개 이상의 매개변수를 갖는 함수형 인터페이스가 필요하다면 직접 정의해야한다.
@FunctionalInterface
interface TriFunction<T,U,V,R> {
R apply(T t, U u, V v);
}
Unary = 단항, Binary = 이항, Operator = 연산자
Function의 또 다른 변형으로 UnaryOperator와 BinaryOperator가 있는데, 매개변수의 타입과 반환타입의 타입이 모두 일치한다는 점만 제외하고는 Function과 같다.
※ UnaryOperator와 BinaryOperator의 조상은 각각 Function/ BiFunction임.
여러 조건식을 논리 연산자(&&, ||, !)로 연결해서 하나의 식을 구성할 수 있는 것처럼, Predicate도 and(), or(), negate()로 연결하여 하나의 새로운 Predicate로 결합할 수 있다.
Predicate 인터페이스에는 default 메서드로 and(), or(), negate()메서드가 정의되어 있다.
해당 메서드들을 아래와 같은 방법으로 결합해 논리 연산자 처럼 사용할 수 있다.
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i % 2 == 0;
Predicate<Integer> notP = p.negate(); // i >= 100
// 100 <= i && (i < 200 || i % 2 ==0)
Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150); // true;
그리고 static메서드인 isEqaul()은 두 대상을 비교하는 Predicate를 만들 때 사용한다. 먼저, isEqual()의 매개변수로 비교대상을 하나 지정하고, 또 다른 비교대상은 test()의 매개변수로 지정한다.
Predicate<String> p = Predicate.isEqual(str1);
boolean result = p.test(str2); // str1과 str2가 같은지 비교하여 결과를 반환.
위 두문장을 하나로 결합 ↓
// str1과 str2가 같은지 비교
boolean result = Predicate.isEqual(str1).test(str2);
import java.util.function.*;
public class FunctionCombineExample {
public static void main(String[] args) {
Function<String, Integer> f = s -> Integer.parseInt(s);
Function<Integer, String> g = i -> Integer.toBinaryString(i);
Function<String, String> h = f.andThen(g);
Function<Integer, Integer> h2 = f.compose(g);
System.out.println(h.apply("FF")); // "FF" -> 255 -> "11111111"
System.out.println(h2.apply(2)); // 2 -> "10" -> 16
Function<String, String> f2 = x -> x; // 항등 함수
System.out.println(f2.apply("AAA")); // AAA가 그대로 출력
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i % 2 == 0;
Predicate<Integer> notP = p.negate(); // i >= 100;
Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150));
String str1 = "abc";
String str2 = "abc";
// str1과 str2가 같은지 비교한 결과를 반환
Predicate<String> p2 = Predicate.isEqual(str1);
boolean result = p2.test(str2);
System.out.println(result);
}
}
위 예제에서는 Function<T, R> 인터페이스도 결합이 가능한 것을 보여준다.