JAVA 8(1) - 함수형 인터페이스와 람다 표현식

3 분 소요

함수형 인터페이스와 람다 표현식

함수형 인터페이스

  • 추상 메소드를 하나만 가지고 있는 인터페이스
  • SAM (Single Abstract Method) 인터페이스
  • @FuncationInterface 애노테이션

람다 표현식

  • 이름이 없는 익명 함수를 단순화함

  • 함수형 인터페이스의 인스턴스를 만드는 방법으로 사용 가능
  • 코드를 줄일 수 있음
  • 메소드 매개변수, 리턴 타입, 변수로 만들어 사용 가능

함수형 프로그래밍

  • 함수를 First class object로 사용 가능

  • 순수 함수 (Pure function)

    함수에서 가장 중요한 것은 입력받은 값이 동일하다면 같은 결과값이 나와야한다. 이런 결과를 보장하지 못한다면 함수형 프로그래밍이라고 할 수 없다.

    • 사이드 이팩트가 없다 (함수 밖에 있는 값을 변경하지 않음)
    • 상태가 없다
  • 고차 함수 (Higher-Order Function)

    • 함수가 함수를 매개변수로 받을 수 있고 함수를 리턴할 수도 있음 (자바에서는 함수가 특수한 형태의 Object이기 때문에 가능)
  • 불변성

자바에서 제공하는 함수형 인터페이스


java 패키지

자바에서 미리 정의해둔 자주 사용할 만한 함수 인터페이스

Function<T,R>
  • T 타입을 받아서 R 타입을 리턴하는 함수 인터페이스
  • 함수 조합용 메소드
    • andThen
    • compose
BiFunction<T,U,R>
  • 두 개의 값(T,U)를 받아서 R 타입을 리턴하는 함수 인터페이스
Consumer<\T>
  • T 타입을 받아서 아무것도 리턴하지 않는 함수 인터페이스
  • 함수 조합용 메소드
    • andThen
Supplier<\T>
  • T 타입의 값을 제공하는 함수 인터페이스
Predicate<\T>
  • T 타입을 받아서 boolean을 리턴하는 함수 인터페이스
  • 함수 조합용 메소드
    • And
    • Or
    • Negate
UnaryOperator<\T>
  • Function<T,R>의 특수한 형태로, 입력값 하나를 받아서 동일한 타입을 리턴하는 함수 인터페이스
BinaryOperator<\T>
  • BiFunction<T,U,R>의 특수한 형태로, 동일한 타입의 입력값 두개를 받아 동일한 타입을 리턴하는 함수 인터페이스

람다


  • (인자 리스트) -> {바디}

인자 리스트 (파라미터)

  • 인자가 없을 때 : ()
  • 인자가 한개일 때: (one) 또는 one
  • 인자가 여러개일 때: (one, two)
  • 인자의 타입은 생략 가능, 컴파일러가 추론하지만 명시도 가능

바디

  • 화살표 오른쪽에 함수 본문을 정의
  • 한 줄인 경우 {}생략 가능, return도 생략 가능

변수캡처 (Variable Capture)

  • 로컬 변수 캡처

    • final이거나 effective final 인 경우에만 참조가능
    • 그렇지 않을 경우 concurrency 문제가 생길수 있어서 컴파일 방지
  • effective final

    java8 부터 허용된 개념.

    기존에는 final 키워드를 붙혀야만 익명클래스로 로컬클래스에서 사용이 가능했으나, final을 붙이지 않더라도 해당 변수의 값을 변경하지 않는다면 사실상 final로 컴파일러가 인식함. (-> 로컬클래스, 익명클래스, 람다에서 사용가능)

    • final 키워드를 사용하지 않은 변수를 익명 클래스 구현체 또는 람다에서 참조할 수 있음
    • “사실상” final 인 변수
  • 익명 클래스나 로컬 클래스와 달리 ‘쉐도잉’을 하지 않는다

    • 익명 클래스와 로컬 클래스는 새로 스콥을 만들지만, 람다는 감싸고 있는 스콥과 같다
    private void run(){
        int baseNumber = 10;
          
        //로컬 클래스
        class LocalClass {
            void printBaseNumber(){
                // 로컬 클래스는 run()과 다른 새로운 스콥이기때문에 같은 이름의 변수를 선언하는 것이 가능
                int baseNumber = 11;
                System.out.println(baseNumber);
            }
        }
          
        // 익명 클래스
        Consumer<Integer> integerConsumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer baseNumber) {
                System.out.println(baseNumber);
            }
        }
          
          
        // 람다
        // 람다는 run() 스콥과 같기 때문에 아래와 같이 인자를 baseNumber이름으로 받을 수 없다. (Variable 'baseNumber' is already defined in the scope) 오류 발생
        IntConsumer printInt = (baseNumber) -> {
            Systemp.out.println(baseNumber);
        };
    }
    

:: 콜론이 두개면 메소드 레퍼런스이다

메소드 레퍼런스

람다가 기존 메소드 또는 생성자를 호출한다면, 메소드 레퍼런스를 사용해서 간결하게 표현이 가능하다.

  • 메소드 참조하는 방법
참조할 메소드 or 생성자 방법
스태틱 메소드 참조 타입::스태틱 메소드
특정 객체의 인스턴스 메소드 참조 객체 레퍼런스::인스턴스 메소드
임의 객체의 인스턴스 메소드 참조 타입::인스턴스 메소드
생성자 참조 타입::new

예시)

  • 기존에 구현된 메소드

    public class Greeting {
          
        private String name;
          
        public Greeting() {}
          
        public Greeting(String name) {
            this.name = name;
        }
          
        public String hello(String name) {
            return "hello " + name;
        }
          
        public static String hi(String name) {
            return "hi " + name;
        }
    }
    
  • 스태틱 메소드 참조 예시

// 람다가 해야할 일이 기존에 구현된 static 메소드인 hi와 같다
UnaryOperator<String> hi = (s) -> "hi " + s;
// 이럴경우 이렇게 구현 가능
UnaryOperator<String> hi = Greeting::hi;
  • 특정 객체의 인스턴스 메소드 참조 예시
// 기존의 hello 인스턴스 메소드 참조
UnaryOperator<String> hello = Greeting::hello;
// apply()를 해야 "jinsoo"라는 값이 Greeting 이라는 인스턴스에 있는 hello 메소드에 전달이 되고 hello jinsoo 가 출력됨
System.out.println(hello.apply("jinsoo"));
  • 생성자 참조 예시
// 입력값 없는 기본 생성자 메소드 참조
Supplier<Geeting> newGreeting = Greeting::new;
// 아래처럼 get()으로 생성을 해야 만들어짐. 람다를 작성한다고 만들어지는 것은 아님
Greeting greeting = newGreeting.get();

// 입력값이 있는 생성자 메소드 참조 (입력값:string, return값:Greeting)
Function<String, Greeting> jinsooGreeting = Greeting::new;

카테고리:

업데이트:

댓글남기기