Java
Java Stream & Function API
goodbye
2022. 12. 21. 17:46
함수형 프로그래밍(Functional Programming)
함수형 프로그래밍
- 함수형 프로그래밍은 하나의 프로그래밍 패러다임으로 정의되는 일련의 코딩 접근 방식이며, 자료처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임 을 의미한다.
- 명령형 프로그래밍 vs 선언형 프로그래밍
명령형 프로그래밍 선언형 프로그래밍 Imperative Programming Declarative Programming OOP 객체 지향 프로그래밍 Functional Programming How to do? What to do? 어떻게 하여야 하는가? 무엇을 하여야 하는가?
- 유저 리스트가 주어졌을때, 검증되지 않은 유저들의 이메일을 리스트로 전달하라는 내용
- 명령형 프로그래밍 : How to do ?
- 이메일을 담을 리스트 선언
- 루프
- 유저선언
- 검증되지 않았는지 체크
- 않았다면 변수에 이메일 추출
- 이메일 리스트에 넣기
- 선언형 프로그래밍 : What to do?
- 유저리스트에서
- 검증되지 않은 유저만 골라내서
- 이메일을 추출해서
- 리스트로 받기
- 유저리스트에서
- 명령형 프로그래밍 : How to do ?
- 1급 시민으로서의 함수 : Function as First-Class Citizen
- 1급 시민의 조건
- 함수 / 메서드의 매개변수(parameter)로서 전달할 수 있는가
- 함수 / 메서드의 반환값(return)이 될 수 있는가
- 변수에 담을 수 있는가
- 1급 시민의 조건
- 함수형 프로그래밍을 통해 얻을수 있는것
- 역할에 충실한 코드
- 가독성이 좋은 코드
- 유지 / 보수에 용이
- 버그로부터 안전
- 확장성에 용이
- 패러다임의 전환
- Stream, Optional …
- 다양한 가능성
- 역할에 충실한 코드
Function Interface
- Function Interface - 1등 시민이 된 함수
- T → Input Type
- R → Return Type
@FunctionalInterface
public interface Funtion<T, R>
R applay(T t);
}
- 함수를 오브젝트 형태로 구현 예



- 함수의 구성내용
- 함수의 이름
- 반환값의 타입 (return type)
- 매개변수 (parameters)
- 함수의 내용 (body)
- Lambda Expresstion
- 위에서 작성했던 Object 함수 클래스를 람다 표현식으로 표현하면 아래와 같다
(Integer x) -> { return x + 10; }
BiFunction Interface
- 매개변수가 두개일때 사용
- T, U → 두개의 Input Parameter Type
- R → Return Type
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}

Functional Interface : 함수의 뼈대
- 단 하나의 abstract method 만을 가지는 인터페이스 : Single Abstract Method Interface
- Default method 와 static method 는 이미 구현되어 있으므로 있어도 괜찮다
- java.lang.Runnable
- java.util.Comparator
- java.util.concurrent.Callable
- etc…
@FunctionalInterface
public interface Function<T, R> {
R apply(T t); // abstract method
default <V> Function<V, R> compose(Function<? super V, ? extends T> before{
...
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after{
...
}
static <T> Function<T, T> identity() {
...
}
}
Functional Interface : 메서드 종류
Supplier : Input 파라미터 없이 Return 만 하는 메서드 (아낌없이 주는 나무)
@FunctionalInterface
public interface Supplier<T>{
T get();
}

Consumer : Input 파라미터만 있고 Retrun 하지 않는 메서드 (먹보)
@FunctionalInterface
public interface Consumter<T>{
void acept(T t);}
구현 예
- Consumer 를 사용한 메서드의 변경없이 넘기는 인자값만 변경하면 다른 결과값 출력
- Parameter 타입을 <T> 로 받으면 다른 자료형의 매개변수도 출력 가능
BiComsumer : 두개의 Input Parameter 를 가지고 리턴하지 않는 추상메서드 제공


Pridicate : True or False 값 (Boolean) 을 리턴
- negate() : 해당 조건을 만족하지 않는
- or() : 해당 조건을 만족하는 값도 포함
- and() : 해당 조건을 만족하는 값만 포함


Comparator : 비교를 위한 인터페이스


Method Reference
1. 클래스의 static method 를 지정할때 : ClassName::staticMethodName
- 사용예
Function<String, Integer> strToInt = Integer::parseInt; int five = strToInt.apply("5");
BiFunction 인터페이스를 이용하여 아래와 같이 구현해볼수 있다

2. 객체의 instace method 를 지정할때 : ClassName::instanceMethodName
- 객체를 생성하고 해당 객체의 메서드를 호출할때 아래와 같이 사용할수 있다

- 해당 클래스의 메서드를 사용하려는 경우 this 를 통해 접근도 가능하다

3. 선언된 객체의 instance method 를 지정 : objectName::instaceMethodName
- 해당 클래스의 인스턴스를 매개변수로 넘겨 메서드를 실행해주는 함수
a. 사용예
Function<String, Integer> strLength = String::length; int length = strLength.apply("Hello world"); BiPredicate<String, String> strEquals = String::equals; boolean result = strEquals.test("hello", "world");
4. 클래스의 constuctor 를 지정할때 : ClassName::new
해당 클래스의 생성자를 호출하는 예
Stream
- 데이터의 흐름
- 컬렉션(Colllection) 형태로 구성된 데이터를 람다를 이용해 간결하고 직관적으로 프로세스
- For, While 등을 잉요하던 기존 Loop 대체
- 손쉽게 병렬 처리 가능
Filter
- 만족하는 데이터만 걸러내는데 사용
- Predicate 에 true 를 반환하는 데이터만 존재하는 stream 을 리턴한다
Stream<T> filter(Predicate<? super T> predicate);



- 검증되지 않은 유저만 출력 : isVerified → false



- 에러상태의 주문만 출력 : status → ERROR



Map
- 데이터를 변형하는데 사용
- 데이터에 해당 함수가 적용된 결과무을 제공하는 Stream 리턴
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
- stream 은 재사용할수 없다
- 재사용하려고 하면 아래와 같이 에러가 발생한다


- 유저의 이메일만 출력


- 유저의 생성아이디를 중복제거하여 출력


구성요소
- Source : 컬렉션, 배열 등
- Intermediate Operations : 0 개 이상의 filter, map 등 중간처리
- Terminal Operation : 종결처리 - collect, reduce
- Error 상태의 오더중에서 24시간 이내에 생성된 주문정보만 출력


정렬
- 데이터가 순서대로 정렬된 상태로 stream 리턴
- 데이터의 종류에 따라 Comparator 필요
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
- 유저를 이름순으로 정렬

- 오더를 생성시간순으로 정렬

중복제거
- 중복되는 데이터가 제거된 stream 리턴
Stream<T> distinct();
- 숫자 중복 제거


- 주문정보 중 유저아이디를 중복제거하고 정렬


FlatMap
- Map + Flatten
- 데이터에 함수를 적용한 후 중척된 stream 을 연결하여 하나의 stream 리턴
<R> Stream<R> flatMap (
Function<? super T,
? extends Stream<? extends R>> mapper);
- 중첩된 배열 데이터를 하나의 리스트로 변환하려고 할때
- map 으로 스트림 리스트를 반환하면 원하는 결과를 얻을수 없다


- faltMap() 메서드를 이용해서 리턴하면 아래와 같이 하나의 리스트로 반환된다


- 오더정보의 오더라인 리스트 데이터들을 하나의 리스트로 출력
Uploaded by N2T