1개의 추상 메소드를 갖는 인터페이스. 주로 콜백 함수나 처리기 등을 표현하는 데 유용
@FunctionalInterface
public interface MyFunction {
void apply(); // 단 하나의 추상 메서드
}
...
//사용 방식
MyFunction myFunc = () -> System.out.println("Hello, Lambda!");
Predicate isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true
(2) Consumer : 하나의 인자(T)를 받아 반환값 없이 처리하는 함수형 인터페이스
Consumer printer = s -> System.out.println(s);
printer.accept("Hello, Consumer!"); // "Hello, Consumer!" 출력
(3) Supplier : 인자를 받지 않고 값을 반환하는 함수형 인터페이스입니다.
Supplier randomValue = () -> Math.random();
System.out.println(randomValue.get()); // 랜덤한 값 출력
(4) Function<T,R> : 하나의 인자를 받아 다른 값을 반환하는 함수형 인터페이스(T -> R)
Function<Integer, String> intToString = num -> "Number: " + num;
System.out.println(intToString.apply(5)); // "Number: 5"
(5) Comparator : 두 인자를 비교하여 정렬 순서를 결정하는 함수형 인터페이스
List numbers = Arrays.asList(5, 2, 8, 1, 3);
Comparator ascendingOrder = (a, b) -> a - b;
Collections.sort(numbers, ascendingOrder)
(6) Runnable : 인자가 없고 반환값도 없는 run() 메서드를 가진 함수형 인터페이스
Runnable runnable = () -> System.out.println("Running...");
new Thread(runnable).start();
(7) Collable : Runnable과 달리 결과를 반환할 수 있는 제너릭 함수형 인터페이스
Callable task = () -> {
System.out.println("Task is executing in thread: " + Thread.currentThread().getName());
return 42; // 작업 결과 반환
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future future = executor.submit(task);
TIP. 이외에도 직접 선언해서 만들어서 사용해도 된다.
| 함수형 인터페이스 | 추상 메서드 |
|---|---|
Runnable | run() |
Callable<V> | call() |
Comparator<T> | compare(T o1, T o2) |
Predicate<T> | test(T t) |
Function<T, R> | apply(T t) |
Consumer<T> | accept(T t) |
Supplier<T> | get() |
UnaryOperator<T> | apply(T t) |
BinaryOperator<T> | apply(T t1, T t2) |
BiFunction<T, U, R> | apply(T t, U u) |
BiPredicate<T, U> | test(T t, U u) |
BiConsumer<T, U> | accept(T t, U u) |
IntFunction<R> | apply(int value) |
IntPredicate | test(int value) |
메서드 레퍼런스는 특정 메서드만을 호출하는 람다의 축약형
| 람다 표현식 | 메서드 레퍼런스 |
|---|---|
(Apple a) → a.getWeight() | Apple::getWeight |
() → Thread.currentThread().dumpStack() | Thread.currentThread()::dumpStack |
(str, i) ⇒ str.substring(i) | String::substring |
(String s) → System.out.println(s) | System.out::println |
자주쓰이는 것으로 정리
(1) Comparator : thenComparing 메서드 사용. 첫 번째 비교 후, 두 객체가 같다고 판단되면 두 번째 비교.
(2) Predicate : negate, and, or 세 가지 메서드로 조합
(3) Function : andThen, compose 두 가지 메서드로 조합(andThen만 추천)
| 명칭 | 용도 | 예시 | 초기 데이터 | 예시 적용 후 데이터 |
|---|---|---|---|---|
| 스트림 생성 | 컬렉션, 배열, 특정 값 등에서 스트림을 생성하는 방법 | List<String> list = Arrays.asList("apple", "banana");list.stream(); | ["apple", "banana"] | Stream["apple", "banana"] |
| 중간 연산 (Intermediate Operations) | 스트림의 데이터를 변형하거나 필터링하는 연산 | filter(), map(), sorted(), distinct() 등 | ||
| filter() | 주어진 조건을 만족하는 요소만 필터링 | list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList()); | ["apple", "banana", "avocado"] | ["apple", "avocado"] |
| map() | 각 요소를 변환하는 연산 | list.stream().map(String::length).collect(Collectors.toList()); | ["apple", "banana", "avocado"] | [5, 6, 7] |
| flatMap() | 중첩된 스트림을 평면화하여 처리 | list.stream().flatMap(List::stream).collect(Collectors.toList()); | [[1, 2], [3, 4], [5, 6]] | [1, 2, 3, 4, 5, 6] |
| sorted() | 스트림 요소를 정렬하는 연산 | list.stream().sorted().collect(Collectors.toList()); | ["banana", "apple", "cherry"] | ["apple", "banana", "cherry"] |
| distinct() | 중복된 요소를 제거하는 연산 | list.stream().distinct().collect(Collectors.toList()); | ["apple", "banana", "apple"] | ["apple", "banana"] |
| peek() | 스트림의 각 요소를 소비하지 않고 디버깅용으로 중간에 확인하는 연산 | list.stream().peek(System.out::println).collect(Collectors.toList()); | ["apple", "banana", "cherry"] | apple banana cherry (콘솔 출력) |
| 최종 연산 (Terminal Operations) | 스트림을 종료하고 결과를 생성하는 연산 | collect(), forEach(), reduce(), anyMatch() 등 | ||
| collect() | 스트림의 요소들을 컬렉션으로 수집 | list.stream().collect(Collectors.toList()); | ["apple", "banana", "cherry"] | ["apple", "banana", "cherry"] |
| forEach() | 스트림의 각 요소를 처리하는 연산 | list.stream().forEach(System.out::println); | ["apple", "banana", "cherry"] | apple banana cherry (콘솔 출력) |
| reduce() | 스트림의 모든 요소를 하나의 값으로 합치는 연산 | list.stream().reduce(0, Integer::sum); | [1, 2, 3, 4] | 10 |
| anyMatch() | 스트림에서 하나라도 조건을 만족하는 요소가 있는지 확인 | list.stream().anyMatch(s -> s.startsWith("a")); | ["apple", "banana"] | true |
| allMatch() | 스트림의 모든 요소가 조건을 만족하는지 확인 | list.stream().allMatch(s -> s.length() > 3); | ["apple", "banana"] | true |
| noneMatch() | 스트림의 요소가 조건을 하나도 만족하지 않는지 확인 | list.stream().noneMatch(s -> s.startsWith("z")); | ["apple", "banana"] | true |
| findFirst() | 스트림에서 첫 번째 요소를 찾는 연산 | Optional<String> first = list.stream().findFirst(); | ["apple", "banana", "cherry"] | Optional[apple] |
| findAny() | 스트림에서 아무거나 첫 번째 요소를 찾는 연산 | Optional<String> any = list.stream().findAny(); | ["apple", "banana", "cherry"] | Optional[apple] |
| count() | 스트림의 요소 개수를 반환하는 연산 | long count = list.stream().count(); | ["apple", "banana", "cherry"] | 3 |
| min() | 스트림에서 최소값을 찾는 연산 | Optional<String> min = list.stream().min(String::compareTo); | ["apple", "banana", "cherry"] | Optional[apple] |
| max() | 스트림에서 최대값을 찾는 연산 | Optional<String> max = list.stream().max(String::compareTo); | ["apple", "banana", "cherry"] | Optional[cherry] |
| toArray() | 스트림의 요소를 배열로 변환 | String[] array = list.stream().toArray(String[]::new); | ["apple", "banana", "cherry"] | ["apple", "banana", "cherry"] |
| flatMapToInt(), flatMapToDouble(), flatMapToLong() | 기본 타입의 스트림으로 변환하여 평면화 | list.stream().flatMapToInt(s -> IntStream.range(1, s.length())); | ["apple", "banana"] | [1, 2, 3, 4, 5] (int 타입 스트림) |
| 병렬 스트림 (Parallel Streams) | 데이터를 병렬로 처리하여 성능을 향상시킬 수 있는 방법 | list.parallelStream().forEach(System.out::println); | ["apple", "banana", "cherry"] | apple banana cherry (콘솔 출력) |
| Stream.of() | 인자로 주어진 데이터를 기반으로 스트림을 생성 | Stream<String> stream = Stream.of("apple", "banana", "cherry"); | ["apple", "banana", "cherry"] | Stream["apple", "banana", "cherry"] |
| iterate() | 지정된 규칙에 따라 반복해서 요소를 생성하는 스트림 생성 | Stream<Integer> stream = Stream.iterate(1, n -> n + 1).limit(5); | [] (초기값 없음) | [1, 2, 3, 4, 5] |
| generate() | 지정된 함수에 의해 무한하게 스트림을 생성 | Stream<Double> stream = Stream.generate(Math::random).limit(5); | [] (초기값 없음) | [0.123, 0.234, 0.345, 0.456, 0.567] |
선언형 : 더 간결하고 가독성이 좋아진다.
조립할 수 있음 : 유연성이 좋아진다.
병렬화 : 성능이 좋아진다.
- Collector 인터페이스 구현은 스트림의 요소를 어떤 식으로 도출할지 지정한다.
| 명칭 | 용도 | 초기 데이터 | 예시 코드 | 예시 적용 후 데이터 |
|---|---|---|---|---|
| toList() | 스트림의 요소들을 List로 수집 | ["apple", "banana", "cherry"] | java List<String> words = Arrays.asList("apple", "banana", "cherry"); List<String> wordList = words.stream().collect(Collectors.toList()); System.out.println(wordList); | ["apple", "banana", "cherry"] |
| toSet() | 스트림의 요소들을 Set으로 수집 | ["apple", "banana", "apple", "cherry"] | java List<String> words = Arrays.asList("apple", "banana", "apple", "cherry"); Set<String> wordSet = words.stream().collect(Collectors.toSet()); System.out.println(wordSet); | ["banana", "apple", "cherry"] (중복 제거) |
| toMap() | 스트림의 요소들을 Map으로 수집, 키와 값을 생성하는 함수 제공 | ["apple", "banana", "cherry"] | java List<String> words = Arrays.asList("apple", "banana", "cherry"); Map<String, Integer> wordLengthMap = words.stream().collect(Collectors.toMap(word -> word, String::length)); System.out.println(wordLengthMap); | {"apple": 5, "banana": 6, "cherry": 6} (단어 길이를 값으로 사용) |
| joining() | 스트림의 요소들을 하나의 문자열로 결합. 구분자, 프리픽스, 서픽스를 지정할 수 있음 | ["apple", "banana", "cherry"] | java List<String> words = Arrays.asList("apple", "banana", "cherry"); String result = words.stream().collect(Collectors.joining(", ")); System.out.println(result); | "apple, banana, cherry" (구분자: ", ") |
| groupingBy() | 스트림의 요소들을 주어진 기준으로 그룹화하여 Map 형태로 반환 | ["apple", "banana", "cherry", "date", "fig"] | java List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "fig"); Map<Integer, List<String>> groupedByLength = words.stream().collect(Collectors.groupingBy(String::length)); System.out.println(groupedByLength); | {3=[fig], 4=[date], 5=[apple, grape], 6=[banana, cherry]} (글자 길이 기준) |
| partitioningBy() | 스트림의 요소들을 조건에 맞는 두 그룹으로 나누어 Map<Boolean, List>로 반환 | [1, 2, 3, 4, 5, 6] | java List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); Map<Boolean, List<Integer>> partitioned = numbers.stream().collect(Collectors.partitioningBy(n -> n % 2 == 0)); System.out.println(partitioned); | {false=[1, 3, 5], true=[2, 4, 6]} (홀수/짝수 기준) |
| counting() | 그룹화된 데이터의 개수를 셈 | ["apple", "banana", "cherry", "date", "fig"] | java List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "fig"); Map<Integer, Long> wordCountByLength = words.stream().collect(Collectors.groupingBy(String::length, Collectors.counting())); System.out.println(wordCountByLength); | {5=1, 6=2, 4=1, 3=1} (글자 길이 기준으로 각 그룹의 개수 계산) |
| summarizingInt() | IntStream의 요약 통계를 계산 (합계, 평균, 최댓값, 최솟값, 개수) | [1, 2, 3, 4, 5] | java List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); IntSummaryStatistics stats = numbers.stream().collect(Collectors.summarizingInt(Integer::intValue)); System.out.println(stats); | IntSummaryStatistics{count=5, sum=15, min=1, average=3.0, max=5} |
| reducing() | 스트림의 요소들을 결합하여 단일 값으로 축소 (예: 합계, 곱셈 등) | [1, 2, 3, 4, 5] | java List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream().collect(Collectors.reducing(0, Integer::sum)); System.out.println(sum); | 15 (합계 계산) |
| summarizingDouble() | DoubleStream의 요약 통계를 계산 (합계, 평균, 최댓값, 최솟값, 개수) | [1.5, 2.5, 3.5, 4.5, 5.5] | java List<Double> numbers = Arrays.asList(1.5, 2.5, 3.5, 4.5, 5.5); DoubleSummaryStatistics stats = numbers.stream().collect(Collectors.summarizingDouble(Double::doubleValue)); System.out.println(stats); | DoubleSummaryStatistics{count=5, sum=17.5, average=3.5, min=1.5, max=5.5} |
| summarizingLong() | LongStream의 요약 통계를 계산 (합계, 평균, 최댓값, 최솟값, 개수) | [1L, 2L, 3L, 4L, 5L] | java List<Long> numbers = Arrays.asList(1L, 2L, 3L, 4L, 5L); LongSummaryStatistics stats = numbers.stream().collect(Collectors.summarizingLong(Long::longValue)); System.out.println(stats); | LongSummaryStatistics{count=5, sum=15, average=3.0, min=1, max=5} |
| mapping() | 스트림의 각 요소를 주어진 함수에 의해 변환하고, 결과를 다른 컬렉션으로 수집 | ["apple", "banana", "cherry"] | java List<String> words = Arrays.asList("apple", "banana", "cherry"); Set<Integer> wordLengths = words.stream().collect(Collectors.mapping(String::length, Collectors.toSet())); System.out.println(wordLengths); | {5, 6, 4} (각 단어의 길이를 Set으로 수집) |
| flatMapping() | Stream의 요소를 변환한 후, 그 결과를 평탄화하여 수집 | [[1, 2], [3, 4]] | java List<List<Integer>> lists = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4)); List<Integer> flatList = lists.stream().collect(Collectors.flatMapping(Collection::stream, Collectors.toList())); System.out.println(flatList); | [1, 2, 3, 4] (리스트를 평탄화하여 List로 수집) |
| toConcurrentMap() | 스트림의 요소들을 ConcurrentMap으로 수집 | ["apple", "banana", "cherry"] | java List<String> words = Arrays.asList("apple", "banana", "cherry"); Map<String, Integer> wordLengthMap = words.stream().collect(Collectors.toConcurrentMap(word -> word, String::length)); System.out.println(wordLengthMap); | {"apple": 5, "banana": 6, "cherry": 6} (단어 길이를 값으로 사용) |
| filtering() | 스트림의 요소들을 필터링하고 특정 조건을 만족하는 요소들만 수집 | ["apple", "banana", "cherry", "date", "fig"] | java List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "fig"); Set<String> result = words.stream().collect(Collectors.filtering(word -> word.startsWith("b"), Collectors.toSet())); System.out.println(result); | [banana] (글자 'b'로 시작하는 단어들만 필터링) |
- collect 메서드는 결과를 **누적**하는 컨테이너를 변경하도록 설계된 메서드
- reduce 메서드는 두 값을 **하나**로 도출하는 **불변형** 연산하는 메서드
// 인터페이스 정의
interface MyInterface {
// 디폴트 메서드
default void defaultMethod() {
System.out.println("디폴트 메서드가 호출되었습니다.");
}
// 추상 메서드
void abstractMethod();
}
// 인터페이스를 구현하는 클래스
class MyClass implements MyInterface {
@Override
public void abstractMethod() {
System.out.println("추상 메서드가 구현되었습니다.");
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
// 추상 메서드 호출
obj.abstractMethod();
// 디폴트 메서드 호출
obj.defaultMethod();
}
}
결과 :
추상 메서드가 구현되었습니다.
디폴트 메서드가 호출되었습니다.
만약 MyClass에 defaultMethod 매서드를 선언해서 사용할 경우는 오버라이딩된다.
class MyClass implements MyInterface {
...
@Override
public void abstractMethod() {
System.out.println("추상 메서드가 구현되었습니다.");
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.abstractMethod();
obj.defaultMethod();
}
}
추상 메서드가 구현되었습니다.
MyClass에서 오버라이드한 디폴트 메서드가 호출되었습니다.
list.sort(comparing(Sample::getCode)
// .reversed()
.thenComparing(Sample::getQty)
.reversed());
comparing 함수를 통해 정렬을 할 수 있다.
reversed가 있으면 역순으로도 정렬이 되며, thenComparing를 이용하여 앞의 경우가 동일할 때의 정렬 순서도 정할 수 있다.