Master Functional & Marker Interfaces with Interactive Examples
A Functional Interface is an interface that contains exactly one abstract method. It can have multiple default and static methods, but only one abstract method.
@FunctionalInterface
public interface Calculator {
// Only one abstract method
int calculate(int a, int b);
// Default methods are allowed
default void printResult(int result) {
System.out.println("Result: " + result);
}
// Static methods are allowed
static void info() {
System.out.println("Calculator Interface");
}
}
Lambda expressions provide a clear and concise way to represent functional interfaces using an expression.
// Traditional way (Anonymous Inner Class)
Calculator traditionalCalc = new Calculator() {
@Override
public int calculate(int a, int b) {
return a + b;
}
};
// Lambda Expression way
Calculator lambdaCalc = (a, b) -> a + b;
// Method Reference way
Calculator methodRefCalc = Integer::sum;
Try different lambda operations:
| Interface | Method | Purpose | Example |
|---|---|---|---|
| Predicate<T> | test(T t) | Boolean-valued function | x -> x > 5 |
| Function<T,R> | apply(T t) | Transform T to R | s -> s.length() |
| Consumer<T> | accept(T t) | Consume input | System.out::println |
| Supplier<T> | get() | Supply values | () -> new Date() |
import java.util.function.*;
import java.util.Arrays;
import java.util.List;
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// Custom Functional Interface
Processor processor = data -> data.toUpperCase();
System.out.println(processor.process("hello")); // HELLO
// Using with Streams
List<String> names = Arrays.asList("alice", "bob", "charlie");
names.stream()
.map(processor::process)
.forEach(System.out::println);
// Built-in Functional Interfaces
Predicate<Integer> isEven = n -> n % 2 == 0;
Function<String, Integer> getLength = String::length;
Consumer<String> printer = System.out::println;
Supplier<Double> randomValue = Math::random;
}
}
@FunctionalInterface
interface Processor {
String process(String data);
}
A Marker Interface is an empty interface (no methods or fields) that provides metadata about a class. It's used to mark or tag classes with specific characteristics.
// Example of a marker interface
public interface Serializable {
// Empty - no methods or fields
}
// Usage
public class Person implements Serializable {
private String name;
private int age;
// Class implementation
}
| Interface | Purpose | Package | Usage |
|---|---|---|---|
| Serializable | Object can be serialized | java.io | I/O operations |
| Cloneable | Object can be cloned | java.lang | Object cloning |
| Remote | Object can be used remotely | java.rmi | RMI operations |
| RandomAccess | List supports fast random access | java.util | Collection optimization |
// Custom marker interface
interface Loggable {
// Empty marker interface
}
interface Cacheable {
// Another marker interface
}
// Implementation
class DatabaseService implements Loggable, Cacheable {
private String data;
public void processData() {
System.out.println("Processing data...");
}
}
// Usage in framework code
public class ServiceManager {
public void handleService(Object service) {
// Check for marker interfaces
if (service instanceof Loggable) {
System.out.println("Logging enabled for: " + service.getClass().getSimpleName());
}
if (service instanceof Cacheable) {
System.out.println("Caching enabled for: " + service.getClass().getSimpleName());
}
}
}
| Aspect | Functional Interface | Marker Interface |
|---|---|---|
| Methods | Exactly one abstract method | No methods at all |
| Purpose | Define behavior/operation | Provide metadata/tagging |
| Usage | Lambda expressions, method references | instanceof checks, framework integration |
| Examples | Runnable, Callable, Predicate | Serializable, Cloneable, Remote |
| Since Version | Java 8 (enhanced) | Java 1.0 |
| Annotation | @FunctionalInterface (optional) | No specific annotation |
// Marker interface for special processing
interface Priority {
}
// Functional interface for processing logic
@FunctionalInterface
interface TaskProcessor {
void process(String task);
}
// Implementation combining both concepts
class ImportantTask implements Priority {
private String name;
public ImportantTask(String name) {
this.name = name;
}
public String getName() { return name; }
}
// Usage in a task manager
class TaskManager {
public void executeTasks(List<Object> tasks, TaskProcessor processor) {
for (Object task : tasks) {
if (task instanceof Priority) {
System.out.println("High priority task detected!");
}
if (task instanceof ImportantTask) {
ImportantTask importantTask = (ImportantTask) task;
processor.process(importantTask.getName());
}
}
}
public static void main(String[] args) {
TaskManager manager = new TaskManager();
List<Object> tasks = Arrays.asList(
new ImportantTask("Database Backup"),
new ImportantTask("Security Update")
);
// Using lambda expression with functional interface
manager.executeTasks(tasks, task ->
System.out.println("Processing: " + task)
);
}
}