// FunctionalInterfaces.java — the built-in functional interfaces and your own.
import java.util.function.*;

public class FunctionalInterfaces {

    // Your own functional interface: exactly one abstract method
    @FunctionalInterface
    interface PriceRule {
        double apply(double price);

        // default methods are allowed and don't count
        default PriceRule then(PriceRule next) {
            return p -> next.apply(apply(p));
        }
    }

    public static void main(String[] args) {
        // Predicate<T>: T -> boolean
        Predicate<String> isSku = s -> s.matches("[A-Z]{2}-\\d{3}");
        System.out.println(isSku.test("AX-200"));         // true
        System.out.println(isSku.negate().test("hello")); // true

        // Function<T, R>: T -> R
        Function<String, Integer> length = String::length;
        Function<Integer, Integer> doubled = n -> n * 2;
        System.out.println(length.andThen(doubled).apply("Boomi"));   // 10

        // Supplier<T>: () -> T  (lazy values)
        Supplier<String> runId = () -> "RUN-" + System.nanoTime() % 1000;
        System.out.println(runId.get().startsWith("RUN-"));           // true

        // Consumer<T>: T -> void
        Consumer<String> log = msg -> System.out.println("[log] " + msg);
        log.andThen(msg -> System.out.println("[audit] " + msg)).accept("order created");

        // BiFunction<T, U, R> and BinaryOperator<T>
        BiFunction<Integer, Double, Double> lineTotal = (qty, price) -> qty * price;
        BinaryOperator<Integer> max = BinaryOperator.maxBy(Integer::compare);
        System.out.println(lineTotal.apply(24, 8.5) + " " + max.apply(3, 9));   // 204.0 9

        // UnaryOperator<T>: T -> T
        UnaryOperator<String> shout = s -> s.toUpperCase() + "!";
        System.out.println(shout.apply("hello"));                    // HELLO!

        // Primitive specialisations avoid boxing
        IntPredicate even = n -> n % 2 == 0;
        System.out.println(even.test(4));                            // true

        // Our own interface, composed
        PriceRule discount = p -> p * 0.9;
        PriceRule tax = p -> p * 1.2;
        System.out.printf("%.2f%n", discount.then(tax).apply(100));  // 108.00
    }
}
