Skip to Content
Course content

68: Functional Interfaces

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've spent a lot of time reviewing junior PRs, and there is one specific point of confusion that keeps popping up when we hit functional programming in Java: the belief that the @FunctionalInterface annotation is what actually "turns" an interface into a functional one.

The Myth: The Annotation Grants the Power

A lot of developers think that if they don't add @FunctionalInterface to the top of their interface, they can't use a lambda expression to implement it. They treat it like a magic switch. Let's look at this code:

public interface SimpleValidator {
    boolean validate(String value);
}

public class Main {
    public static void main(String[] args) {
        // I didn't use the annotation, but this still works perfectly.
        SimpleValidator isNotEmpty = (s) -> s != null && !s.isEmpty();
        System.out.println(isNotEmpty.validate("Hello")); // true
    }
}

As you can see, the code compiles and runs without a hitch. The annotation isn't a requirement for the functionality; the compiler doesn't actually need it to allow lambdas.

The Reality: The Single Abstract Method (SAM) Rule

The only thing that actually makes an interface "functional" is that it has exactly one abstract method. That's it. I call this the SAM rule (Single Abstract Method). If there is one—and only one—method that doesn't have an implementation, Java can safely map a lambda's parameters and return type to that specific method.

So, why does the @FunctionalInterface annotation even exist? Think of it as a safety guard. If I mark an interface with that annotation and then a teammate (or a future, tired version of myself) tries to add a second abstract method, the compiler will throw an error immediately. It prevents us from accidentally breaking every lambda implementation of that interface across the entire codebase.

It's worth noting that you can have as many default or static methods as you want. Since those have bodies, they don't count against your "one abstract method" quota.

Stop Inventing Your Own Interfaces

Now, here is a piece of professional advice: just because you can write your own functional interfaces doesn't mean you should. In the early days of Java 8, we wrote a lot of our own. Now? It's mostly noise. Java provides a robust set of built-in functional interfaces in the java.util.function package that cover 95% of use cases.

  • Predicate<T>: Takes one argument, returns a boolean. (Perfect for filtering).
  • Consumer<T>: Takes one argument, returns void. (Great for printing or saving).
  • Function<T, R>: Takes one argument, returns a result of a different type. (The classic mapper).
  • Supplier<T>: Takes nothing, returns a value. (Useful for lazy initialization).

If you find yourself writing public interface MyCustomChecker { boolean check(String s); }, stop. Use Predicate<String> instead. It makes your code instantly more readable to other Java engineers because they already know exactly what a Predicate does without having to go find your interface definition.




📋 Practical Task

Implementing a Dynamic Product Discount Engine

You are building a checkout system for an e-commerce store. Instead of hard-coding discount logic, you need to create a system where different discount strategies can be passed into a processor.

Your Task:

  1. Create a functional interface named DiscountStrategy with a single method: double applyDiscount(double price).
  2. Add the @FunctionalInterface annotation to ensure the interface remains functional.
  3. Create a class DiscountProcessor with a method double calculateFinalPrice(double originalPrice, DiscountStrategy strategy) that applies the passed strategy to the price.
  4. In your main method, use the DiscountProcessor to calculate the final price for a product costing $100.00 using three different lambda expressions:
    • A "10% Off" discount.
    • A "Flat $20 Off" discount.
    • A "No Discount" (return price as is) strategy.

Ensure your output clearly prints the result of each of the three different strategies.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.