함수형 인터페이스는 단 하나의 추상 메서드만을 가지는 인터페이스입니다. Java 8부터 도입된 람다 표현식과 함께 사용되어 함수형 프로그래밍을 가능하게 합니다.
@FunctionalInterface 어노테이션으로 명시 (선택사항이지만 권장)@FunctionalInterface
public interface MyFunction {
void execute();
// default 메서드는 허용
default void printInfo() {
System.out.println("MyFunction Interface");
}
// static 메서드도 허용
static void staticMethod() {
System.out.println("Static method");
}
}
이 어노테이션을 사용하면 컴파일러가 함수형 인터페이스 규칙을 검증해주므로 실수를 방지할 수 있습니다.
입력을 받아서 결과를 반환합니다.
Function<String, Integer> stringLength = str -> str.length();
Integer length = stringLength.apply("Hello"); // 5
입력을 받아서 boolean 값을 반환합니다.
Predicate<Integer> isPositive = num -> num > 0;
boolean result = isPositive.test(10); // true
입력을 받아서 처리하지만 반환값이 없습니다.
Consumer<String> printer = str -> System.out.println(str);
printer.accept("Hello World"); // "Hello World" 출력
입력 없이 값을 반환합니다.
Supplier<Double> randomValue = () -> Math.random();
Double value = randomValue.get();
두 개의 입력을 받아서 결과를 반환합니다.
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Integer sum = add.apply(5, 3); // 8
Function<T, T>의 특수한 형태로, 입력과 출력 타입이 같습니다.
UnaryOperator<Integer> square = num -> num * num;
Integer result = square.apply(5); // 25
BiFunction<T, T, T>의 특수한 형태로, 두 입력과 출력 타입이 모두 같습니다.
BinaryOperator<Integer> multiply = (a, b) -> a * b;
Integer result = multiply.apply(4, 5); // 20
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class FunctionalInterfaceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// Predicate를 사용한 필터링
List<String> filtered = names.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
System.out.println(filtered); // [Alice, Charlie, David]
// Function을 사용한 변환
List<Integer> lengths = names.stream()
.map(name -> name.length())
.collect(Collectors.toList());
System.out.println(lengths); // [5, 3, 7, 5]
// Consumer를 사용한 처리
names.forEach(name -> System.out.println("Hello, " + name));
}
}
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
public class CustomFunctionalInterface {
public static void main(String[] args) {
// 덧셈
Calculator add = (a, b) -> a + b;
System.out.println("5 + 3 = " + add.calculate(5, 3)); // 8
// 곱셈
Calculator multiply = (a, b) -> a * b;
System.out.println("5 * 3 = " + multiply.calculate(5, 3)); // 15
// 메서드에 전달
int result = performOperation(10, 5, (a, b) -> a - b);
System.out.println("10 - 5 = " + result); // 5
}
public static int performOperation(int a, int b, Calculator calc) {
return calc.calculate(a, b);
}
}
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
class Product {
private String name;
private double price;
private String category;
public Product(String name, double price, String category) {
this.name = name;
this.price = price;
this.category = category;
}
public String getName() { return name; }
public double getPrice() { return price; }
public String getCategory() { return category; }
@Override
public String toString() {
return name + " ($" + price + ")";
}
}
public class ProductProcessor {
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product("Laptop", 1200.0, "Electronics"),
new Product("Phone", 800.0, "Electronics"),
new Product("Desk", 300.0, "Furniture"),
new Product("Chair", 150.0, "Furniture")
);
// Predicate: 가격 필터
Predicate<Product> expensiveItems = p -> p.getPrice() > 500;
// Function: 가격에 세금 추가
Function<Product, Double> addTax = p -> p.getPrice() * 1.1;
// Consumer: 제품 정보 출력
Consumer<Product> printProduct = p ->
System.out.println(p.getName() + ": $" + p.getPrice());
// 실제 사용
System.out.println("고가 제품:");
products.stream()
.filter(expensiveItems)
.forEach(printProduct);
System.out.println("\n세금 포함 가격:");
products.stream()
.forEach(p -> System.out.println(
p.getName() + ": $" + addTax.apply(p)
));
// 메서드 체이닝
double totalPrice = products.stream()
.filter(p -> p.getCategory().equals("Electronics"))
.mapToDouble(Product::getPrice)
.sum();
System.out.println("\n전자제품 총액: $" + totalPrice);
}
}
import java.util.*;
class Employee {
private String name;
private int age;
private double salary;
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() { return name; }
public int getAge() { return age; }
public double getSalary() { return salary; }
@Override
public String toString() {
return name + " (Age: " + age + ", Salary: $" + salary + ")";
}
}
public class ComparatorExample {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", 30, 70000),
new Employee("Bob", 25, 60000),
new Employee("Charlie", 35, 80000)
);
// 나이순 정렬
employees.sort((e1, e2) -> e1.getAge() - e2.getAge());
System.out.println("나이순:");
employees.forEach(System.out::println);
// 급여순 정렬 (내림차순)
employees.sort((e1, e2) -> Double.compare(e2.getSalary(), e1.getSalary()));
System.out.println("\n급여순 (높은순):");
employees.forEach(System.out::println);
// Comparator 메서드 사용
employees.sort(Comparator.comparing(Employee::getName));
System.out.println("\n이름순:");
employees.forEach(System.out::println);
}
}
import java.util.*;
import java.util.function.*;
public class OptionalExample {
public static void main(String[] args) {
Optional<String> name = Optional.of("John");
// Consumer: 값이 있으면 처리
name.ifPresent(n -> System.out.println("Hello, " + n));
// Function: 값 변환
Optional<Integer> nameLength = name.map(n -> n.length());
System.out.println("Name length: " + nameLength.get());
// Supplier: 값이 없을 때 기본값 제공
Optional<String> empty = Optional.empty();
String result = empty.orElseGet(() -> "Default Name");
System.out.println(result); // "Default Name"
// Predicate: 조건 필터링
name.filter(n -> n.startsWith("J"))
.ifPresent(n -> System.out.println("Name starts with J: " + n));
}
}
함수형 인터페이스는 메서드 참조와 함께 사용할 수 있어 코드를 더 간결하게 만들 수 있습니다.
import java.util.*;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 람다 표현식
names.forEach(name -> System.out.println(name));
// 메서드 참조 (더 간결함)
names.forEach(System.out::println);
// 정적 메서드 참조
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.map(String::valueOf) // Integer::toString 대신
.forEach(System.out::println);
}
}
함수형 인터페이스는 Java에서 함수형 프로그래밍을 가능하게 하는 핵심 개념입니다. Stream API, Optional, Comparator 등과 함께 사용하여 더 선언적이고 간결한 코드를 작성할 수 있습니다.