22/03/10 TIL

Jaden.dev·2022년 3월 10일
0

📌 람다와 함수형 인터페이스

함수형 프로그래밍

  • 요약하자면 함수를 parameter로 넘길 수 있도록 함
  • 독립적인 함수는 side-effect가 없기 때문에 Thread-safe
  • 함수형 프로그래밍에서는 함수를 1급객체로 간주하여 parameter로 넘기거나 return 할 수 있음

익명 클래스

  • 프로그램에서 일시적으로 한번만 사용되고 버려지는 객체
  • 재사용성이 없음
  • 다음과 같은 경우 사용하면 용이
    • 프로그램 내에서 단발성으로 사용되는 객체 (ex. UI 이벤트처리, 스레드 객체 등)
    • 재사용할 일이 없고, 확장성을 활용하는 것이 유지보수에서 불리할 때. 비즈니스 로직이 제각각인데, 재사용성이 없어 매번 클래스를 생성하는 비용이 더 많을 때.

람다 표현식

  • 메서드의 구성 요소는 다음과 같다.
    • name
    • parameters
    • body(implement)
    • return type
  • 위 4가지는 메서드의 필수 규격이지만, name과 return type은 그 우선순위가 떨어짐
  • 따라서 생략하여 코드를 단순화 함
public class ThreadExample {
  public static void main(Stringp[ args) {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello World");
        }
    });
    
    thread.start();
  }
}
  • 위 코드에서 new Runnable() 은 익명 클래스의 선언 부분이다. Thread의 생성자 parameter는 이미 Runnable 인터페이스가 유일하므로, 생략하더라도 컴파일러가 자체적으로 Runnable, 또는 이를 구현한 클래스라고 예측할 수 있다.
Thread thread = new Thread(
  @Override
  public void run() {
    System.out.println("Hello World");
  }
);
  • 메서드 명과 리턴타입을 제거해보자.
Thread thread = new Thread(
  { System.out.println("Hello World"); }
);
  • 마지막으로 람다 문법으로 정리하면 다음과 같이 정리된다.
Thread thread = new Thread( () -> System.out.println("Hello World") );
  • Runnable 의 메서드 void run() 은 받는 parameter가 없어 비어있지만, parameter가 존재한다면 -> 앞부분에 존재하는 () 에 parameter가 들어갈 것이다.
  • 정리하자면, 람다 표현식은 ->을 기준으로 왼쪽에 parameter, 오른쪽이 body(implement) 영역
  • 값(혹은 객체)의 특징을 가져, 메서드의 parameter로 전달하거나 변수로 참조할 수 있다.
  • 람다 표현식 자체를 재사용할 필요가 있을 때, 위에서 표현한 코드를 다음과 같이 고쳐볼 수 있다.
Runnable runImpl = () -> System.out.println("Hello World");
Thread thread = new Thread(runImpl);
  • 메서드의 리턴 타입으로도 사용할 수 있다.
public Runnable getRunnable() {
  return () -> System.out.println("Hello World");
}
Runnable runImpl = getRunnable();
Thread thread = new Thread(runImpl);
  • 주의할 점은, return 뒤에 있는 람다 표현식은 함수형 인터페이스인 Runnable 인터페이스를 정의하는 부분이고, System.out.println 이 실행되는 부분은 아니다.
  • 람다 표현식이 실행되는 부분은 정의한 곳이 아니라, 정의한 함수형 인터페이스의 메서드를 호출하는 부분이다.
  • 위 예제에서는 runImpl 에서 run() 메서드가 호출되어야 실행된다.

함수형 인터페이스

  • 익명 클래스를 구현하는 인터페이스는 통상적으로 여러 개의 메서드를 포함하고 있음
  • 람다 표현식은 메서드 이름이 없는데 컴파일러가 어떻게 인식하는 것일까?
  • 결과적으로 람다 표현식을 쓸 수 있는 인터페이스는 public 메서드 하나만 가지고 있어야
  • 이러한 형태의 인터페이스를 함수형 인터페이스라고 부른다.
  • 함수형 인터페이스에서 제공하는 단 하나의 추상 메서드(public)을 함수형 메서드라고 함
  • 앞서 공부했던 interface default 메서드는 존재해도 상관없다.
  • 오직 하나의 public 메서드와, 여러개의 default, static, private 메서드로 이루어진 인터페이스 역시 람다 표현식을 이용할 수 있음

java.util.function

  • 람다 표현식을 이용하기 위해서는 interface의 public 메서드가 1개만 존재해야하는데, 이렇게 정의하기 번거로울 수 있다. 이를 위해 java8에서는 많이 사용되는 4가지 유형의 함수형 인터페이스를 제공함
  • Consumer<T\>
    • void accept(T t)
    • 파라미터를 전달하고 처리한 후, 리턴 X
  • Function<T, R>
    • R apply(T t)
    • 전달할 파라미터를 다른 값으로 변환하여 리턴. 값 변경이나 매핑할 때 사용
  • Predicate<T\>
    • boolean test(T t)
    • 전달받은 값에 대해 true/false 값을 리턴. 데이터 필터링이나 조건 체크에 사용
  • Supplier<T\>
    • T get()
    • 파라미터 없이 리턴 값만 있는 경우 사용

메서드 & 생성자 참조

📌 추가로 알게된 점들

추상 메서드와 일반 메서드

  • 추상메서드는 interface에 선언된 구현이 완료되지 않은 메서드, 일반 메서드는 class에서 구현 완료된 메서드

기본형 데이터와 참조형 데이터

  • 참조형 데이터는 박싱/언박싱의 과정때문에 쓸데없는 리소스 사용이 있을 수 있다. 이를 위해 Int~ Double~ 등의 하위 클래스를 자바에서 제공하고 있음 (ex. 함수형 인터페이스로 제공되는 IntToLongFunction 이나, 스트림API의 IntStream 등)
profile
https://github.com/JadenHeo

0개의 댓글