API가 다루는 개념의 수가 줄어들어 익히기 더 쉬워진다.
표준 함수형 인터페이스들은 유용한 디폴트 메서드를 많이 제공하므로 다른 코드와의 상호운용성도 크게 좋아진다.

UnaryOperator<T>
public class UnaryOperatorExample {
public static void main(String[] args) {
List<String> words = new ArrayList<>(Arrays.asList("banana", "apple", "cherry"));
System.out.println("Before conversion: " + words);
// Using UnaryOperator to convert the list to uppercase
**UnaryOperator<String> toUpperCase = String::toUpperCase;**
List<String> upperCaseWords = words.stream()
.map(toUpperCase)
.collect(Collectors.toList());
System.out.println("After conversion: " + upperCaseWords);
}
}
BinaryOperator<T>
public class BinaryOperatorExample {
public static void main(String[] args) {
// Using BinaryOperator to concatenate two strings
BinaryOperator<String> concatenate = (s1, s2) -> s1 + s2;
**String result = concatenate.apply("Hello, ", "world!");**
System.out.println(result); // Output: Hello, world!
}
}
Predicate<T>
public class PredicateExample {
public static void main(String[] args) {
List<String> words = new ArrayList<>(Arrays.asList("banana", "apple", "cherry", "blueberry"));
System.out.println("Original list: " + words);
// Using Predicate to filter the list for words that start with 'b'
Predicate<String> startsWithB = s -> s.startsWith("b");
List<String> filteredWords = words.stream()
.filter(startsWithB)
.collect(Collectors.toList());
System.out.println("Filtered list: " + filteredWords);
}
}
Function<T>
public class FunctionExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("banana", "apple", "cherry");
System.out.println("Original list: " + words);
// Using Function to convert the list of strings to their lengths
Function<String, Integer> stringLength = String::length;
List<Integer> lengths = words.stream()
.map(stringLength)
.toList();
System.out.println("Lengths: " + lengths);
}
}
Supplier<T>
public class SupplierExample {
public static void main(String[] args) {
// Using Supplier to generate a random number
Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
// Getting a random number
Integer randomNumber = randomSupplier.get();
System.out.println("Random number: " + randomNumber);
}
}
Consumer<T>
리턴을 받지 않고 넣어주기만 하는 경우
public class ConsumerExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("banana", "apple", "cherry");
System.out.println("Original list: " + words);
// Using Consumer to print each element of the list
Consumer<String> printWord = System.out::println;
words.forEach(printWord);
}
}
자주 쓰이며, 이름 자체가 용도를 명확히 설명해준다.
반드시 따라야 하는 규약이 있다.
유용한 디폴트 메서드를 제공할 수 있다.
@FunctionalInterface
• 그 인터페이스가 람다용으로 설계된 것임을 알려준다. • 해당 인터페이스가 추상 메서드를 오직 하나만 가지고 있어야 컴파일 되게 해준다. • 유지보수 과정에서 누군가 실수로 메서드를 추가하지 못하게 막아준다
@FunctionalInterface
public interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
===
public class TriFunctionExample {
public static void main(String[] args) {
// Using TriFunction to concatenate three strings
TriFunction<String, String, String, String> concatenate = (s1, s2, s3) -> s1 + s2 + s3;
String result = concatenate.apply("Hello, ", "world", "!");
System.out.println(result); // Output: Hello, world!
// Using TriFunction to sum three integers
TriFunction<Integer, Integer, Integer, Integer> sum = (a, b, c) -> a + b + c;
Integer sumResult = sum.apply(1, 2, 3);
System.out.println(sumResult); // Output: 6
}
}