Advanced Interfaces and Lambda
This lesson covers advanced interface features and Lambda expressions to make your code more concise.
Interface Default Methods
Java 8 introduced default methods, allowing interfaces to have method bodies.
Syntax
JAVA
public interface MyInterface {
// Abstract method
void abstractMethod();
// Default method
default void defaultMethod() {
System.out.println("Default implementation");
}
}
Example: Default Methods
JAVA
public interface Vehicle {
void start();
default void horn() {
System.out.println("Beep beep!");
}
default void stop() {
System.out.println("Vehicle stopped");
}
}
public class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car started");
}
// Can optionally override default methods
@Override
public void horn() {
System.out.println("Honk honk!");
}
}
public class DefaultMethodDemo {
public static void main(String[] args) {
Car car = new Car();
car.start(); // Car started
car.horn(); // Honk honk! (overridden method)
car.stop(); // Vehicle stopped (interface default)
}
}
Interface Static Methods
Java 8 introduced static methods that are called directly through the interface name.
JAVA
public interface MathUtils {
static int add(int a, int b) {
return a + b;
}
static int multiply(int a, int b) {
return a * b;
}
}
public class StaticMethodDemo {
public static void main(String[] args) {
System.out.println(MathUtils.add(3, 5)); // 8
System.out.println(MathUtils.multiply(3, 5)); // 15
}
}
Functional Interfaces
An interface with only one abstract method can be implemented with a Lambda expression.
@FunctionalInterface Annotation
JAVA
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
}
Common Functional Interfaces
| Interface | Method | Description |
|---|---|---|
Predicate<T> |
boolean test(T t) |
Tests a condition |
Function<T,R> |
R apply(T t) |
Transforms input to output |
Consumer<T> |
void accept(T t) |
Consumes input |
Supplier<T> |
T get() |
Provides output |
Lambda Expressions
Lambda is a shorthand for anonymous functions, introduced in Java 8.
Syntax
JAVA
(parameters) -> { body }
Example: Lambda
JAVA
// Traditional approach
Calculator add = new Calculator() {
@Override
public int calculate(int a, int b) {
return a + b;
}
};
// Lambda expression
Calculator add = (a, b) -> a + b;
// Call
int result = add.calculate(3, 5); // 8
Various Lambda Forms
JAVA
// No parameters
Runnable r = () -> System.out.println("Hello");
// One parameter
Consumer<String> print = s -> System.out.println(s);
// Multiple parameters
Comparator<String> comp = (a, b) -> a.length() - b.length();
// Multi-line body
Function<String, String> upper = s -> {
String result = s.trim();
return result.toUpperCase();
};
Example: Using Lambda
JAVA
import java.util.Arrays;
import java.util.List;
import java.util.function.*;
public class LambdaDemo {
public static void main(String[] args) {
// Predicate: test condition
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true
System.out.println(isEven.test(7)); // false
// Function: transform
Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("Hello")); // 5
// Consumer: consume
Consumer<String> print = s -> System.out.println("Output: " + s);
print.accept("Java"); // Output: Java
// Supplier: provide
Supplier<Double> random = () -> Math.random();
System.out.println(random.get());
}
}
Lambda with Collections
Lambda makes collection operations more concise.
Example: Iteration
JAVA
import java.util.Arrays;
import java.util.List;
public class LambdaCollection {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Traditional approach
for (String name : names) {
System.out.println(name);
}
// Lambda approach
names.forEach(name -> System.out.println(name));
// Method reference (more concise)
names.forEach(System.out::println);
}
}
Example: Sorting
JAVA
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class LambdaSort {
public static void main(String[] args) {
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
// Traditional approach
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Lambda approach
names.sort((a, b) -> a.compareTo(b));
// Method reference
names.sort(String::compareTo);
System.out.println(names); // [Alice, Bob, Charlie]
}
}
Method References
Method references are a shorthand for Lambda expressions.
Four Types of Method References
| Type | Syntax | Example |
|---|---|---|
| Static method | ClassName::staticMethod |
Math::abs |
| Instance method | object::instanceMethod |
System.out::println |
| Specific class | ClassName::instanceMethod |
String::length |
| Constructor | ClassName::new |
ArrayList::new |
Example: Method References
JAVA
import java.util.Arrays;
import java.util.List;
import java.util.function.*;
public class MethodRefDemo {
public static void main(String[] args) {
// Static method reference
Function<Integer, Integer> abs = Math::abs;
System.out.println(abs.apply(-5)); // 5
// Instance method reference
Consumer<String> print = System.out::println;
print.accept("Hello"); // Hello
// Specific class instance method reference
Function<String, Integer> length = String::length;
System.out.println(length.apply("Hello")); // 5
// Constructor reference
Supplier<List<String>> listFactory = ArrayList::new;
List<String> list = listFactory.get();
}
}
Comprehensive Example
JAVA
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class LambdaPractice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter even numbers
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Evens: " + evens); // [2, 4, 6, 8, 10]
// Square
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println("Squares: " + squares); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
// Sum
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println("Sum: " + sum); // 55
// Maximum
int max = numbers.stream()
.reduce(Integer::max)
.orElse(0);
System.out.println("Max: " + max); // 10
}
}
❓ Frequently Asked Questions
Q What's the difference between Lambda and anonymous inner classes?
A Lambda is more concise but only works with functional interfaces. Anonymous inner classes can work with any interface or class.
Q When should I use Lambda?
A When implementing functional interfaces, especially for collection operations and event handling.
Q How do I choose between method references and Lambda?
A If Lambda just calls an existing method, use method reference for conciseness.
📖 Summary
- Interface default methods: introduced in Java 8, interfaces can have method bodies
- Interface static methods: called directly through the interface name
- Functional interfaces: interfaces with only one abstract method
- Lambda expressions: shorthand for anonymous functions
- Method references: shorthand for Lambda
📝 Exercises
- Lambda practice: Use Lambda to convert strings to uppercase, get length, check if empty
- Collection operations: Use Lambda to filter, transform, and sort collections
- Custom functional interface: Define a math operation interface, implement add/subtract/multiply/divide with Lambda
Next Lesson
In the next lesson, we'll learn about Enums and Inner Classes — special classes in Java.



