-
4장 리액티브 프로그래밍을 위한 사전 지식Spring/Webflux 2023. 4. 22. 13:13
1 함수형 인터페이스(Functional Interface)
함수형 인터페이스 역시 인터페이스
함수를 값으로 취급, 어떤 함수를 호출할 때 함수 자체를 파라미터로 전달할 수 있습니다 ??
암호화 화폐를 unit 순서대로 sort 하는 예제
Collections.sort() 이용
Comparator 인터페이스 사용
public static void main(String[] args) { List<CryptoCurrency> cryptoCurrencies = SampleData.cryptoCurrencies; // unit 순서대로 sort Collections.sort(cryptoCurrencies, new Comparator<CryptoCurrency>() { @Override public int compare(CryptoCurrency cc1, CryptoCurrency cc2) { return cc1.getUnit().name().compareTo(cc2.getUnit().name()); } }); for(CryptoCurrency cryptoCurrency : cryptoCurrencies) System.out.println("암호 화폐명: " + cryptoCurrency.getName() + ", 가격: " + cryptoCurrency.getUnit()); }
Comparator 내부에는 compare() 라는 단 하나의 추상 메서드가 정의 ( default 메서드 제외 )
@FunctionalInterface 애너테이션 사용
@FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); default Comparator<T> reversed() { return Collections.reverseOrder(this); } default Comparator<T> thenComparing(Comparator<? super T> other) { Objects.requireNonNull(other); return (Comparator<T> & Serializable) (c1, c2) -> { int res = compare(c1, c2); return (res != 0) ? res : other.compare(c1, c2); }; } ...(생략)... }
2 람다 표현식(Lambda Expression)
javascript 에서 함수를 값으로 전달하는 예제
const CryptoCurrencyUnits = [ 'BTC', 'ETH', 'DOT' ]; console.log(CryptoCurrencyUnits.map(unit => unit.length));
Comparator 함수형 인터페이스의 람다 표현 예제
public static void main(String[] args) { List<CryptoCurrency> cryptoCurrencies = SampleData.cryptoCurrencies; Collections.sort(cryptoCurrencies, (cc1, cc2) -> cc1.getUnit().name().compareTo(cc2.getUnit().name())); for(CryptoCurrency cryptoCurrency : cryptoCurrencies) System.out.println("암호 화폐명: " + cryptoCurrency.getName() + ", 가격: " + cryptoCurrency.getUnit()); }
혼 동하지 않아야 될 부분
메서드 자체를 파라미터로 전달하는 것이 아니라, 함수형 인터페이스를 구현한 클래스의 인스턴트를 람다 표현식으로 작성해서 전달한다
Lambda Capturing 람다 캡처링
람다 표현식 외부에서 정의된 변수를 사용 가능
public static void main(String[] args) { List<CryptoCurrency> cryptoCurrencies = SampleData.cryptoCurrencies; String korBTC = "비트코인"; // korBTC = "빗코인"; cryptoCurrencies.stream() .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) .map(cc -> cc.getName() + "(" + korBTC + ")" ) .forEach(cc -> System.out.println(cc)); }
주의 할 점은 외부변수 korBTC 는 final 이여야함
// korBTC = "빗코인"
처럼 변경해서 사용하면 에러 발생
3 메서드 레퍼런스(Method Reference)
static 에서 흔하게 사용
cryptoCurrencies.stream() .map(cc -> cc.getName()) // .map(name -> StringUtils.upperCase(name)) .map(StringUtils::upperCase) .forEach(name -> System.out.println(name));
object :: instance method 유형
cryptoCurrencies.stream() .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) .map(cc -> new ImmutablePair(cc.getPrice(), amount)) // .map(pair -> calculator.getTotalPayment(pair)) .map(calculator::getTotalPayment) .forEach(System.out::println);
ClassName :: new 유형
Optional<PaymentCalculator> optional = cryptoCurrencies.stream() .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) .map(cc -> new ImmutablePair(cc.getPrice(), amount)) // .map(pair -> new PaymentCalculator(pair)) .map(PaymentCalculator::new) .findFirst();
4 함수 디스크립터(Function Descriptor)함수 디스크립터는 함수 서술자, 함수 설명자
일반화된 람다 표현식을 통해서 이 함수형 인터페이스가 어떤 파라미터를 가지고, 어떤 값을 리턴하는지 설명해 주는 역할
함수형 인터페이스 함수 디스크립터 Predicate<T> T -> boolean Consumer<T> T -> void Function<T, R> T -> R Supplier<T> () -> T BiPredicate<L,R> (L, R) -> boolean BiConsumer<T, U> (T, U) -> void BiFunction<T, U. R> (T, U) -> R Predicate<T>
T -> boolean, 구현해야 되는 추상 메소드가 하나의 파라미터를 가지고, 리턴값으로 boolean 타입의 값을 반환하는 함수형 인터페이스
public static void main(String[] args) { List<CryptoCurrency> cryptoCurrencies = SampleData.cryptoCurrencies; List<CryptoCurrency> result = filter(cryptoCurrencies, cc -> cc.getPrice() > 500_000); for (CryptoCurrency cc : result) { System.out.println(cc.getName()); } } private static List<CryptoCurrency> filter(List<CryptoCurrency> cryptoCurrencies, Predicate<CryptoCurrency> p){ List<CryptoCurrency> result = new ArrayList<>(); for (CryptoCurrency cc : cryptoCurrencies) { if (p.test(cc)) { result.add(cc); } } return result; }
Predicate 는 test 메서드를 구현해야 함
@FunctionalInterface public interface Predicate<T> { boolean test(T t); ... }
Consumer<T>
T -> void, 데이터를 소비하는 역할, 리턴값이 없음 void
일정 주기별로 특정 작업을 수행할 때
accept 메서드를 구현해야 함
@FunctionalInterface public interface Consumer<T> { void accept(T t); ... }
public static void main(String[] args) { List<CryptoCurrency> cryptoCurrencies = SampleData.cryptoCurrencies; List<CryptoCurrency> filtered = filter(cryptoCurrencies, cc -> cc.getUnit() == CryptoCurrency.CurrencyUnit.BTC || cc.getUnit() == CryptoCurrency.CurrencyUnit.ETH); addBookmark(filtered, cc -> saveBookmark(cc)); } private static void addBookmark(List<CryptoCurrency> cryptoCurrencies, Consumer<CryptoCurrency> consumer) { for (CryptoCurrency cc : cryptoCurrencies) { consumer.accept(cc); } } private static void saveBookmark(CryptoCurrency cryptoCurrency) { System.out.println("# Save " + cryptoCurrency.getUnit()); }
Function<T,R>
T -> R, 파라미터로 T를 가지며, 리턴값은 R 타입
어떤 처리 과정을 거친 후 그 결과로 특정 타입의 값을 반환하는 전형적인 함수 역할
R 을 반환하는 apply(T t) 메서드를 구현해야 함
@FunctionalInterface public interface Function<T, R> { R apply(T t); ... }
public static void main(String[] args) { ... // 필터된 암호화 화폐를 각각 2개씩 살돈 계산해 int totalPayment = calculatePayment(filtered, cc -> cc.getPrice() * 2); System.out.println("# 구매 비용: " + totalPayment); } private static int calculatePayment(List<CryptoCurrency> cryptoCurrencies, Function<CryptoCurrency, Integer> f) { int totalPayment = 0; for (CryptoCurrency cc : cryptoCurrencies) { totalPayment += f.apply(cc); } Supplier s = () -> ""; return totalPayment; }
Supplier<T>
파라미터를 갖지 않으며, 리턴 값으로 T 타입의 값만 반환
T get() 메서드를 구현해야 함
@FunctionalInterface public interface Supplier<T> { T get(); }
암호화 화폐의 지갑을 복구할 때 사용되는 니모닉 워드를 생성하는 시뮬레이션 코드
public static void main(String[] args) { String mnemonic = createMnemonic(); System.out.println(mnemonic); } private static String createMnemonic() { return Stream .generate(() -> getMnemonic()) .limit(12) .collect(Collectors.joining(" ")); } private static String getMnemonic() { List<String> mnemonic = Arrays.asList( "alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india", "juliet", "kilo", "lima", "mike", "november", "oscar", "papa", "quebec", "romeo", "sierra", "tango", "uniform", "victor", "whiskey", "xray", "yankee", "zulu" ); Collections.shuffle(mnemonic); return mnemonic.get(0); }
BiPredicate / BiConsumer / BiFunction 은 파라미터가 2개
'Spring > Webflux' 카테고리의 다른 글
6장 마블 다이어그램 (1) 2023.04.22 5장 Reactor 개요 (0) 2023.04.22 3장 Blocking I/O 와 Non-Blocking I/O (0) 2023.04.21 2장 리액티브 스트림즈 (0) 2023.04.19 Spring Webflux 공부하기 1 (0) 2023.04.18