ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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

    댓글

Designed by Tistory.