📗 BOOK/스프링으로 시작하는 리액티브 프로그래밍

4장 함수형 인터페이스

미미누 2024. 11. 20. 22:42

함수형 인터페이스(Functional Interface)

  • 단 하나의 추상 메서드만을 가진다.
  • 함수를 값으로 취급하기 때문에 어떤 함수를 호출할 때, 함수 자체를 파라미터로 전달가능하다.

단 하나의 추상 메서드를 가지는 인터페이스 예제

public class Example {
	public static void main(String[] args) {
		List<CryptoCurrency> cryptoCurrencies = SampleData.cryptoCurrencies;
		
		Collections.sort(cryptoCurrencies, new Comparator<CryptoCurrency> () {
			@Override
			public int compare(CryptoCurency c1, CryptoCurrency cc2) {
				return cc1.getUnit().name().compareTo(cc2.getUnit().name());
			}
		});
  • crypthCurrencies 객체를 화폐단위의 오름차순으로 정렬하기 위해 추가적으로 Comparator 인터페이스를 사용
  • Comparator 인터페이스를 익명 구현 객체의 형태로 Collections.sort() 파라미터로 전달하는데, 길어지고 지저분함.
  • 인터페이스의 익명 구현 객체를 전달하는 방식을 함수형 프로그래밍 방식에 맞게 표현한 것을 람다 표현식

람다 표현식은 함수를 값으로 취급하기 위해 표현된 간결한 형태의 표현식이다.

  • 즉 함수형 인터페이스를 구현한 클래스의 메서드 구현을 단순화한 표현식입니다.
(String a, String b) -> e.equals(b)
  • 람다 파라미터, 람다 몸체로 구성
public class Example {
	public static void main(String[] args) {
		List<CryptoCurrency> cryptoCurrencies = SampleData.cryptoCurrencies;
		
		Collections.sort(cryptoCurrencies, 
			(cc1, cc2) -> cc1.getUnit().name().compareTo(cc2.getUnit().name()));
		});
  • 함수형 인터페이스를 구현한 클래스의 인스턴스를 람다 표현식으로 작성해서 전달한다.
  • 람다 표현식 외부에서 정의된 자유변수를 람다 표현식에 사용하는 것을 람다 캡처링이라 한다. (Lambda Capturing)
  • String korBTC = "비트코인"; korBTC = "비트코인2"; cryptoCurrecies.stream() .filter(cc-> cc.getUnit() == CurrenccyUnit.BTC) .map(cc -> cc.getName() + '(' + korBTC + ")" ) .forEach(System.out::println));

메서드 레퍼런스

Car::getCarName
  • 람다 표현식 몸체에 기술된 getCarName()을 메서드 레퍼런스로 표현함
    • ClassName:: static Method 유형
    • String korBTC = "비트코인"; korBTC = "비트코인2"; cryptoCurrecies.stream() .map(cc -> cc.getName()) .map(StringUtills::upperCase) .forEach(name -> System.out.println(name));
    • ClassName:: instance Method 유형
    • String korBTC = "비트코인"; korBTC = "비트코인2"; cryptoCurrecies.stream() .map(cc -> cc.getName()) .map(String::upperCase) .forEach(name -> System.out.println(name));
    • object :: Instance method 유형
    • PaymentCalculator calculator = new PaymentCalculator(); cryptoCurrecies.stream() .filter(cc-> cc.getUnit() == CurrenccyUnit.BTC) .map(cc > new ImmutablePair(cc.getPrice(), amount)) .map(pair -> calculator.getTotalPayment(pair)) .map(calcultor::getTotalPayment) .forEach(System.out::println);
    • ClassName :: new 유형
    • Optional<PaymentCalcultor> optioanl = cryptoCurrencies.stream() .filter(cc -> cc.getUnit() == CurrencyUnit.BTC) .map(cc -> new ImmutablePair(cc.getPrice(), amount)) .map(PaymentCalcultor::new) .findFirst();

함수 디스크립터

함수 디스크립터는 함수 서술자, 함수 설명자 정도로 이해 가능한데, 일반화된 람다 표현식을 통해서 이 함수형 인터페이스가 어떤 파라미터를 가지고, 어떤 값을 리턴하는지 설명하는 역할을 한다.

  • Predicate
    • T → boolean
      public class Examle {
      	public static void main(String[] agrgs) {
      		List<CryptoCurrency> result = filter(cryptoCurrencies, cc -> cc.getPrice() > 500000);
      
      	private static List<CryptoCurrency> filter(List<CryptoCurrency> CryptoCurrencies,
      						Predicate<CryptoCurrency> p) {
      							List<CryptoCurrecy> result = new ArrayList<>();
      							for (CryptoCurrency cc : cryptoCurrencies) {
      								if (p.test(cc)){
      									result.add(cc);
      								}
      						return result;
      					}
      				}
      		
      
    • @FunctionalInterface public interface Predicate<T> { boolean test(T t); }
    • Consumer 함수형 인터페이스
      • T → void
      • 일정 주기별로 특정 작업을 수행한 후, 결과 값을 리턴할 필요가 없는 경우가 대부분인 배치처리
      @FunctionalInterface
      public interface Consumer<T> {
      	void accept(T t);
      }
      
    • Function 함수형 인터페이스
      • T → R
      • Java 스트림의 Map 메서드
      @FuncitonalInterface
      public interface Funciton<T, R> {
      	R apply (T t);
      }
      	
      
    • Supplier
    • @FuncitonalInterface public interface Supplier<T> { T get(); }
    • Bixxxxx
      • 추상 메서드에 전달하는 파라미터가 하니나 더 추가되어 두 개의 파라미터를 가짐.