The full picture
- Functional interface = exactly one abstract method (SAM);
@FunctionalInterfaceenforces it. Know the core set:Function,BiFunction,Supplier,Consumer,Predicate,UnaryOperator+ primitive specializations (IntFunction,ToLongFunction…) that avoid boxing. - Capture semantics: locals are captured by value (hence effectively final); fields are read through the captured
this— mutable and racy if you share the lambda across threads. - Method reference forms:
Type::staticMethod,instance::method,Type::instanceMethod(receiver becomes first arg),Type::new. - invokedynamic benefit: the JVM can optimize dispatch and avoid class-file bloat; capturing lambdas allocate, non-capturing ones don't — relevant in hot paths.
// 1. 'this' differs from anonymous classes
Runnable r = () -> System.out.println(this); // enclosing object
Runnable r2 = new Runnable() {
public void run() { System.out.println(this); } // the anonymous instance
};
// 2. Boxing in disguise on hot paths
Function<Integer,Integer> f = x -> x * 2; // boxes every call
IntUnaryOperator g = x -> x * 2; // primitive, no allocation