CH8 - Lambdas and FI Flashcards
Lambda expression
LE - is a block of code that gets passed around. You can think of a LE as an unnamed method existing inside a anonymous class.
Lambdas
✅ Lambdas can be used to implement functional interfaces (interfaces with one abstract method).
✅ Lambdas can call default methods but cannot override them.
❌ Lambdas cannot override or redefine default methods; you must use a concrete or anonymous class instead.
Anonymous class
✅ Anonymous classes are used for quick, one-time implementations.
✅ They implement interfaces or extend classes (abstract or concrete).
✅ They can override default methods of an interface.
✅ They cannot have a constructor (since they don’t have a name).
Functional Interface
SAM - Single Abstract Method
Functional Interface Rules
✔ Exactly one abstract method (SAM - Single Abstract Method)
✔ Can have default and static methods
✔ Can extend another functional interface (without adding new abstract methods)
✔ @FunctionalInterface annotation is optional but recommended
✔ Methods from Object class do not count as abstract methods
✔ Supports lambda expressions and method references
Adding Object Methods
If a FI includes an abstract method with the same signature as a public method found in Object, those methods do not count toward the single abstract method test.
It is valid FI.
public interface Dive {
//
String toString();
public boolean equals(Object o);
public abstract int hashCode();
public void dive();
}
:: on Lambdas
:: is like a lambda, and it is used for deferred execution with a functional interface. You can even imagine the method reference a s a lambda if it helps you.
Formats of method reference
- static methods
- Instance methods on a particular object
- Instance methods on a parameter to be determined at runtime
-Constructors
Consumer FI
You use a Consumer when you want to do something with a parameter but not return anyting.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}</T>
Consumer<String> c1 = System.out::println;</String>
c1.accept(“Annie”); //Annie
Supplier FI
Supplier is used when you want to generate or supply values without taking any input.
@FunctionalInterface
public interface Supplier<T> {
T get();
}</T>
Supplier<LocalDate> s1 = LocalDate::new;
s1.get() //2025-02-20</LocalDate>
BiConsumer FI
Does same thing as Consumer except that it takes two parameters.
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
var map = new HashMap<String, Integer>();
BiConsumer<String, Integer> b1 = map::put;
b1.accept(“Chicken”, 100);
Predicate FI
Predicate is oftern used when filtering or matching.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}</T>
Predicate<String> s1 = String::isEmpty;
sout(p1.test(""); //true</String>
BiPredicate FI
Same as Predicate but accepts two parameters
@FunctionalInterface
public interface BiPredicate<T, U> {
boolean test(T t, U u);
}
BiPredicate<String, String> b1 = String::startsWith;
sout(b1.test(“chicken”, “chick”); //true
Function FI
A Function is responsible for turning one parameter into a value of potentially different type and returning it.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Function<String, Integer> f1 = String::length;
sout(f1.apply(“cluck”)); //5
BiFunction FI
BiFunction is responsible for turning two parameters into a value and returning it.
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
BiFunction<String, String, String> b1 = String::concat;
sout (b1.apply(“baby “, “chick”)); //baby chick
UnaryOperator and BinaryOperator FI
They are special cases of Function.
They require all type parameters to be the same type.
UnaryOperator transforms its value into one of the same type.
BinaryOperator merges two values into one of the same type.
@FunctionalInterface public interface UnaryOperator<T> extends Function<T, T> {
//
}</T>
@FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T, T, T> {
//
}</T>
Variables in Lambdas
They appear in three places:
-parameter list
-Local variables declared inside the lambda body
-variables referenced from lambda body
Parameter List formats in Lambda
-Without types
-With types
-With var
Compiler requires all parameters in the lambda to use the same format
Local variables inside a lambda body
It is legal to define a block inside lambda body.
That block can have anything that is valid in a normal Java block including local variable declarations.
Referencing Variables from Lambda body
Lambda bodies are allowed to reference some variables from the surrounding code.
Lambda can access an instance variable, method parameter, or local variable under certain conditions.
Instance variables (and class variables) are always allowed.
The only thing lambdas cannot access are variables that are not final or effectively final.