2

In an example code like this, my IDE changed my code from using an anonymous class to return a lambda. That's cool but how does it work?

public static Specification<User> getUsersByFirstNameSpec(String name) {
        return new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root,
                                         CriteriaQuery<?> query,
                                         CriteriaBuilder criteriaBuilder) {
                Predicate equalPredicate = criteriaBuilder.like(
                        criteriaBuilder.upper(root.get("firstName")),
                        name.toUpperCase());
                return equalPredicate;
            }
        };
    }

How does java know the type of the 3 parameters?

 public static Specification<User> getUsersByLastNameSpec(String name) {
        return (Specification<User>) (root, query, criteriaBuilder) -> {
            Predicate equalPredicate = criteriaBuilder.like(
                    criteriaBuilder.upper(root.get("lastName")),
                    name.toUpperCase());
            return equalPredicate;
        };
    }
1

1 Answer 1

2

In java, lambdas are interpreted in terms of a so-called 'functional interface'.

A functional interface is any interface (as in, public interface SomeInterface { ... }) which contains precisely 1 'non-Object, non-defaulted' method. So, for each method declared in the interface, disregard it if it is either just repeating something that's already in the spec of java.lang.Object itself (such as, for example, the toString in interface Example { String toString(); }), or which has a default impl, such as the foo in interface Example { default String foo() { return ""; } }. Then, how many methods are left? If the answer is precisely 1 (not 0, not 2), then it is a functional interface.

If you use a lambda (the arrow notation thing, or the typeOrReferenceExpr::someMethod syntax), then the place where you use it MUST be a place where the compiler can figure out precisely which functional interface is intended. If you can't, it's a compiler error. For example:

Object o = () -> System.out.println("Hello!");

this is a compile-time error. But this:

Runnable r = () -> System.out.println("Hello!");

is fine; java.lang.Runnable is a functional interface, because it has only 1 relevant method, namely void run();.

Once the functional interface is clear, the lambda is interpreted as being an implementation of that one method. So, in your example, java knows the types for root, query, and criteriaBuilder because the compiler can figure out that this is an impl of the interface Specification<User>, and that interface has only one (non-Object, non-defaulted) method, whose spec is:

Predicate toPredicate(Root<User>, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)

and that's how java knows. It also figures out any throws clause from this, too, and the return type too.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.