Java 內建函數式介面

Java 8 在 java.util.function 套件中提供了許多常用的函數式介面。

四大核心介面

介面方法參數回傳用途
Function<T,R>apply(T)TR轉換
Consumer<T>accept(T)Tvoid消費
Supplier<T>get()T提供
Predicate<T>test(T)Tboolean判斷

Function

轉換型別或處理資料:

import java.util.function.Function;

// 基本用法
Function<String, Integer> length = s -> s.length();
int len = length.apply("Hello");  // 5

// 方法參考
Function<String, Integer> length2 = String::length;

// 實際應用
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> lengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());  // [5, 3, 7]

組合 Function

Function<String, String> trim = String::trim;
Function<String, String> upper = String::toUpperCase;

// andThen:先執行 trim,再執行 upper
Function<String, String> combined = trim.andThen(upper);
combined.apply("  hello  ");  // "HELLO"

// compose:先執行 upper,再執行 trim
Function<String, String> combined2 = trim.compose(upper);
combined2.apply("  hello  ");  // "  HELLO  "

Consumer

消費資料,不回傳結果:

import java.util.function.Consumer;

// 基本用法
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello");  // 印出 "Hello"

// 方法參考
Consumer<String> printer2 = System.out::println;

// 實際應用
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);

組合 Consumer

Consumer<String> log = s -> System.out.println("[LOG] " + s);
Consumer<String> save = s -> database.save(s);

// andThen:依序執行
Consumer<String> combined = log.andThen(save);
combined.accept("data");  // 先 log,再 save

Supplier

提供資料,不需要輸入:

import java.util.function.Supplier;

// 基本用法
Supplier<String> greeting = () -> "Hello, World!";
String s = greeting.get();  // "Hello, World!"

// 工廠模式
Supplier<List<String>> listFactory = ArrayList::new;
List<String> list = listFactory.get();

// 延遲計算
Supplier<Double> randomSupplier = Math::random;
double value = randomSupplier.get();  // 每次呼叫才計算

// 實際應用:Optional 的 orElseGet
Optional<String> opt = Optional.empty();
String value = opt.orElseGet(() -> computeDefault());

Predicate

判斷條件,回傳 boolean:

import java.util.function.Predicate;

// 基本用法
Predicate<String> isEmpty = s -> s.isEmpty();
boolean result = isEmpty.test("");  // true

// 方法參考
Predicate<String> isEmpty2 = String::isEmpty;

// 實際應用
List<String> names = Arrays.asList("Alice", "Bob", "", "Charlie");
List<String> nonEmpty = names.stream()
    .filter(s -> !s.isEmpty())
    .collect(Collectors.toList());  // ["Alice", "Bob", "Charlie"]

組合 Predicate

Predicate<String> notEmpty = s -> !s.isEmpty();
Predicate<String> longerThan3 = s -> s.length() > 3;

// and
Predicate<String> combined = notEmpty.and(longerThan3);
combined.test("Hello");  // true
combined.test("Hi");     // false

// or
Predicate<String> either = notEmpty.or(longerThan3);

// negate
Predicate<String> empty = notEmpty.negate();

BiFunction

接受兩個參數:

import java.util.function.BiFunction;

BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
int sum = add.apply(3, 5);  // 8

BiFunction<String, String, String> concat = (a, b) -> a + b;
String result = concat.apply("Hello", "World");  // "HelloWorld"

// Map 的 computeIfAbsent
Map<String, Integer> map = new HashMap<>();
map.computeIfAbsent("key", k -> k.length());

BiConsumer

接受兩個參數,無回傳:

import java.util.function.BiConsumer;

BiConsumer<String, Integer> printer = (name, age) -> 
    System.out.println(name + " is " + age);
printer.accept("Alice", 25);  // "Alice is 25"

// Map 的 forEach
Map<String, Integer> map = Map.of("Alice", 25, "Bob", 30);
map.forEach((k, v) -> System.out.println(k + ": " + v));

BiPredicate

接受兩個參數,回傳 boolean:

import java.util.function.BiPredicate;

BiPredicate<String, Integer> lengthCheck = (s, len) -> s.length() == len;
boolean result = lengthCheck.test("Hello", 5);  // true

UnaryOperator

輸入和輸出同型別:

import java.util.function.UnaryOperator;

// UnaryOperator<T> 繼承自 Function<T, T>
UnaryOperator<String> toUpper = String::toUpperCase;
String result = toUpper.apply("hello");  // "HELLO"

UnaryOperator<Integer> square = x -> x * x;
int result2 = square.apply(5);  // 25

// List 的 replaceAll
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.replaceAll(String::toUpperCase);  // ["A", "B", "C"]

BinaryOperator

兩個相同型別的輸入,輸出同型別:

import java.util.function.BinaryOperator;

// BinaryOperator<T> 繼承自 BiFunction<T, T, T>
BinaryOperator<Integer> add = (a, b) -> a + b;
int sum = add.apply(3, 5);  // 8

BinaryOperator<String> concat = (a, b) -> a + b;

// Stream 的 reduce
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, (a, b) -> a + b);  // 15

// 靜態方法
BinaryOperator<Integer> max = BinaryOperator.maxBy(Comparator.naturalOrder());
BinaryOperator<Integer> min = BinaryOperator.minBy(Comparator.naturalOrder());

基本型別特化

避免自動裝箱的效能損失:

// int
IntFunction<String> intToString = i -> String.valueOf(i);
IntConsumer printer = System.out::println;
IntSupplier random = () -> (int)(Math.random() * 100);
IntPredicate isPositive = i -> i > 0;
IntUnaryOperator square = i -> i * i;
IntBinaryOperator add = (a, b) -> a + b;

// long
LongFunction<String> longToString = l -> String.valueOf(l);
LongConsumer longPrinter = System.out::println;

// double
DoubleFunction<String> doubleToString = d -> String.valueOf(d);
DoubleConsumer doublePrinter = System.out::println;

// 轉換
IntToLongFunction intToLong = i -> (long) i;
IntToDoubleFunction intToDouble = i -> (double) i;
ToIntFunction<String> strToInt = Integer::parseInt;
ToLongFunction<String> strToLong = Long::parseLong;
ToDoubleFunction<String> strToDouble = Double::parseDouble;

實際範例

驗證器

public class Validator<T> {
    private final List<Predicate<T>> rules = new ArrayList<>();

    public Validator<T> addRule(Predicate<T> rule) {
        rules.add(rule);
        return this;
    }

    public boolean validate(T value) {
        return rules.stream().allMatch(rule -> rule.test(value));
    }
}

// 使用
Validator<String> passwordValidator = new Validator<String>()
    .addRule(s -> s.length() >= 8)
    .addRule(s -> s.matches(".*[A-Z].*"))
    .addRule(s -> s.matches(".*[0-9].*"));

boolean valid = passwordValidator.validate("Password123");  // true

策略模式

public class PriceCalculator {
    private final Map<String, Function<Double, Double>> strategies = new HashMap<>();

    public PriceCalculator() {
        strategies.put("normal", price -> price);
        strategies.put("vip", price -> price * 0.8);
        strategies.put("svip", price -> price * 0.6);
    }

    public double calculate(String type, double price) {
        return strategies.getOrDefault(type, p -> p).apply(price);
    }
}

事件處理

public class EventBus {
    private final Map<String, List<Consumer<Object>>> listeners = new HashMap<>();

    public void subscribe(String event, Consumer<Object> handler) {
        listeners.computeIfAbsent(event, k -> new ArrayList<>()).add(handler);
    }

    public void publish(String event, Object data) {
        List<Consumer<Object>> handlers = listeners.get(event);
        if (handlers != null) {
            handlers.forEach(h -> h.accept(data));
        }
    }
}

介面總覽

介面參數回傳方法
Function<T,R>TRapply
BiFunction<T,U,R>T, URapply
Consumer<T>Tvoidaccept
BiConsumer<T,U>T, Uvoidaccept
Supplier<T>Tget
Predicate<T>Tbooleantest
BiPredicate<T,U>T, Ubooleantest
UnaryOperator<T>TTapply
BinaryOperator<T>T, TTapply

重點整理

  • Function:轉換,T → R
  • Consumer:消費,T → void
  • Supplier:提供,() → T
  • Predicate:判斷,T → boolean
  • 使用 andThencompose 組合 Function
  • 使用 andornegate 組合 Predicate
  • 基本型別特化版本避免裝箱開銷