스트림은 왜 람다를 사용할 수 있나?
(스트림은 어떻게 람다 표현식을 지원하나?)
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);
IntStream mapToInt(ToIntFunction<? super T> mapper);
Stream<T> sorted(Comparator<? super T> comparator);
void forEach(Consumer<? super T> action);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
...
스트림 인터페이스에는 스트림에서 지원하는 여러 메서드가 추상 메서드로 선언되어 있다.
스트림을 구현하는 클래스는 이러한 스트림 메서드가 구현되어 있다는 것을 유추할 수 있다.
누가 이런 메서드를 구현하고 있는지 역으로 추적을 해 보면...
1. 스트림을 생성하는 Stream.of Static 메서드
2. of를 따라가 보면 Arrays.stream을 호출하고 있고
3. Arrays.stream은 다시 stream 메서드 -> StreamSupport.stream 으로 연결되어 있다.
4. StreamSupport.stream 메소드는 ReferencePipeLine.Head 메서드로 연결되고...
5. ReferencePipeLine의 헤더는 ReferencePipeLine 내부의 Static class 이고, 생성자를 호출하는 것을 알 수 있다.
즉, Steam.of 또는 Arrays.stream은 여러 중간 클래스를 거쳐서 결국 ReferencePipeLine 객체를 생성한다.
6. ReferencePipeLine 클래스
- 정의를 보면 추상 클래스로 이를 상속받은 클래스가 바로 5번에서 언급한 static class인 Head 임을 알 수 있다.
- Implements Stream<P_OUT>으로 Stream Interface에 선언된 메서드를 모두 구현했음을 알 수 있다.
ReferencePipeLine에서 제공되는 메서드 목록을 보면, Steam Interface에 정의된 익숙한 메서드 들이 나열되어 있다.
이렇게 해서 우리는 Arrays로 구성된 배열이 어떻게 스트림으로 만들어지고,
스트림 인터페이스에 정의된 메서드가 어떻게 호출이 될 수 있는지 Java의 Core 코드를 통해서 확인했다.
* 실제 세부 구현까지 모두 확인하는 것은 너무 방대한 작업이라 여기까지만...
스트림 메서드는 대부분 함수형 인터페이스를 인자로 받는다.
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);
IntStream mapToInt(ToIntFunction<? super T> mapper);
Stream<T> sorted(Comparator<? super T> comparator);
void forEach(Consumer<? super T> action);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
...
즉, 스트림 메서드를 이용한 방법 = 함수형 인터페이스를 구현한 메서드를 제공하는 것이며,
다음 4가지 방식을 사용할 수 있다.
스트림에 함수형 인터페이스의 인자(파라미터)를 주입하는 방법
직접 익명 클래스를 만들어 인터페이스를 구현한 메서드를 제공한다.
private <T> void printStream(Stream<T> t) {
t.forEach(System.out::println);
}
@Test
void inheritFilterTest() {
Stream<Student> students = StreamTestGenerator.getPersonStream();
Stream<Student> youngStudents = students.filter(new Predicate<Student>() {
@Override
public boolean test(Student student) {
return student.getAge() < 20;
}
});
printStream(youngStudents);
}
익명 클래스를 생성하는 팩토리 메서드를 통해 제공한다. 1번의 확장 개념
private static Predicate<Student> YoungFilter() {
return new Predicate<Student>() {
@Override
public boolean test(Student t) {
return t.getAge() < 20;
}
};
}
@Test
void StaticMethodOverride() {
Stream<Student> students = StreamTestGenerator.getPersonStream();
Stream<Student> youngStudents = students.filter(YoungFilter());
printStream(youngStudents);
}
함수형 인터페이스를 구현한 클래스의 객체(인스턴스)로 넘긴다.
class YoungFilterClass<T extends Student> implements Predicate<T> {
@Override
public boolean test(T t) {
return t.getAge() < 20;
}
}
@Test
void implementsClassTest() {
Stream<Student> students = StreamTestGenerator.getPersonStream();
Stream<Student> youngStudents = students.filter(new YoungFilterClass<>());
printStream(youngStudents);
students = StreamTestGenerator.getPersonStream();
YoungFilterClass<Student> yf = new YoungFilterClass<>();
youngStudents = students.filter(yf);
printStream(youngStudents);
}
Lambda 표현식 또는 Lambda 표현식을 변수에 할당해서 넘긴다.
@Test
void lambdaTest() {
Stream<Student> students = StreamTestGenerator.getPersonStream();
Stream<Student> youngStudents = students.filter((t)-> t.getAge() < 20 );
printStream(youngStudents);
Predicate<Student> youngFilter = (t) -> t.getAge() < 20;
students = StreamTestGenerator.getPersonStream();
youngStudents = students.filter(youngFilter);
printStream(youngStudents);
}
반복을 줄이기 위해 만든 printStream도 Lambda로 변경한 코드는 아래와 같다.
@Test
void allLambdaTest() {
Stream<Student> students = StreamTestGenerator.getPersonStream();
students.filter((t)-> t.getAge() < 20 )
.forEach(System.out::println);
Consumer printT = (t) -> System.out.println(t);
students = StreamTestGenerator.getPersonStream();
students.filter((t)-> t.getAge() < 20 )
.forEach(printT);
}
'프로그래밍 > Java-Spring' 카테고리의 다른 글
영어 자판으로 입력된 한글 변환 코드 (0) | 2023.08.29 |
---|---|
IntelliJ Selenium 프로젝트 구성 (0) | 2023.04.10 |
Java 함수형 프로그래밍 #2 (0) | 2023.01.17 |
Java 함수형 프로그래밍 (0) | 2023.01.17 |
Java 8 함수형 인터페이스 (Functional Interface) (0) | 2023.01.11 |