람다식(Lambda expression)의 도입

  • 자바는 등장 이후 두 번의 큰 변화가 있었음

    • JDK 1.5 ~ 제네릭스(generics)의 등장

    • JDK 1.8 ~ 람다식(lambda expression)의 등장

  • 람다식의 도입

    • 자바는 객체지향언어인 동시에 함수형 언어

    • 자바는 객체지향언어인데, 함수형 언어의 기능까지 갖추게 한 것
      기존의 자바를 거의 변경하지 않고도, 함수형 언어의 장점을 잘 접목시키는데 성공

  • 메서드(method)와 함수(function)의 차이

    • 객체지향에서는 함수(function) 대신 객체의 행위나 동작을 의미하는 메서드(method)라는 용어를 사용

    • 메서드는 함수와 같은 의미이지만, 특정 클래스에 반드시 속해야 한다는 제약이 있기 때문에 용어를 분리

    • 람다식을 이용하면 메서드가 하나의 독립적인 기능을 하기 때문에 함수라는 용어를 사용할 수 있음



람다식이란?

람다식(Lambda expression)

  • 메서드를 하나의 ‘식(expression)’으로 표현한 것

  • 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해줌

  • 메서드를 람다식으로 표현하면 메서드의 이름과 반환 값이 없어짐

    • 람다식을 ‘익명 함수(anonymous function)’이라고도 함
int[] arr = new int[5];
Arrays.setAll(arr, () -> (int)(Math.random()*5)+1);

// 같은 의미
int method() {
    return (int)(Math.random()*5)+1;
}
  • 위의 ()->(int)(Math.random()*5)+1)이 바로 람다식

  • 메서드 표현보다 람다식이 간결하면서도 이해가 쉬움

    • 메서드일 경우, 메서드는 클래스에 포함되어야 하기 때문에 클래스를 새로 만들어야 할 수 있음

    • 메서드일 경우, 생성한 클래스의 객체도 생성해야만 이 메서드를 호출할 수 있음

    • 람다식은 위의 과정이 없이 람다식 자체만으로도 이 메서드의 역할을 대신할 수 있음

  • 람다식

    • 메서드의 매개변수로 전달되어지는 것이 가능

    • 메서드의 결과로 반환될 수도 있음

    • 람다식으로 인해 메서드를 변수처럼 다루는 것이 가능


람다식 작성하기

  • 람다식은 ‘익명 함수’답게 메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통[] 사이에 ->를 추가
* 메서드
반환타입 메서드이름 (매개변수 선언) {
    문장들
}

* 람다식
               (매개변수 선언) -> {
    문장들
}
  • 변환의 예) 두 값 중 큰 값을 반환하는 메서드 max
// 메서드
int max(int a, int b) {
    return a > b ? a : b;
}

// 람다(1)
       (int a, int b) -> {
    return a > b ? a : b;
}

// 람다(2)
(int a, int b) -> { return a > b ? a : b; }
  • 변환의 예2) 반환값이 있는 경우

    • return문 대신 식(expression)으로 대신 할 수 있음

    • 식의 연산결과가 자동적으로 반환값이 됨

    • 문장(statement)이 아닌 식(expression)이므로 뒤에 ‘;’을 붙이지 않음

// 람다(문장)
(int a, int b) -> { return a > b ? a : b; }

// 람다(식 변환)
(int a, int b) -> a > b ? a : b
  • 람다식에 선언된 매개 변수의 타입은 추론이 가능한 경우 생략 가능

    • 대부분의 경우 생략 가능

    • 람다식에 반환 타입이 없는 이유 : 추론이 가능하기 때문

    • 단, 매개변수 타입 생략시 전체 생략 or 전체 생략하지 않음 (부분 생략 불가)

// 람다(식 변환)
(int a, int b) -> a > b ? a : b

// 람다(매개 변수 타입 생략)
(a, b) -> a > b ? a : b
  • 선언된 매개변수가 하나일 경우, 괄호() 생략 가능

    • 매개변수의 타입이 있다면 괄호() 생략 불가
// 생략 가능
(a) -> a + a
a -> a + a

// 생략 불가
(int a) -> a * a
int a -> a + a 에러!
  • 괄호 {} 안의 문장이 하나일 때는 생략 가능

    • 문장의 끝에 ;를 붙이지 말아야 함

    • return문일 경우 괄호 생략 불가

// 생략 가능, ;를 떼야 함
(String name, int i) -> {
    System.out.println(name + " = " + i);
}

(String name, int i) ->
    System.out.println(name + " = " + i)

// 생략 불가(return)
(int a, int b) -> { return a > b ? a : b; } // OK
(int a, int b) -> return a > b ? a : b; 에러!
(int a, int b) -> a > b ? a : b // OK 식변환


람다식 작성 실습

  • case 1
// 문제
int max(int a, int b) {
    return a > b ? a : b;
}

// 실습 1
(int a, int b) -> { return a > b ? a : b; }

// 실습 2
(int a, int b) -> a > b ? a : b;

// 실습 3
(a, b) -> a > b ? a : b;
  • case 2
// 문제
void printVar(String name, int i) {
    System.out.println(name + " = " + i);
}

// 실습 1

(String name, int i) -> { System.out.println(name + " = " + i); }

// 실습 2

(String name, int i) -> System.out.println(name + " = " + i)

// 실습 3
(name, i) -> System.out.println(name + " = " + i)
  • case 3
// 문제
int square(int x) {
    return x * x;
}

// 실습 1
(int x) -> { return x * x; }

// 실습 2
(

// 실습 3
x -> x * x;
  • case 4
// 문제
int roll() {
    return (int) (Math.random()*6);
}

// 실습 1
() -> { return (int) (Math.random()*6); }

// 실습 2
() -> (int) (Math.random*6);
  • case 5
// 문제
int sumArry(int[] arr) {
    int sum = 0;
    for (int i : arr)
        sum += 1;
    return sum;
}

// 실습
(int[] arr) -> {
    int sum = 0;
    for (int i : arr)
        sum += 1;
    return sum;
}



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

  • 람다식은 사실 익명 클래스의 객체와 동등함
// 람다식
(int a, int b) -> a > b ? a : b

// 익명클래스
new Object() {
    int 메서드(int a, int b) {
        return a > b ? a : b;
    }
}
  • 익명 객체의 메서드 호출

    • 참조변수가 있어야 객체의 메서드 호출 가능

    • 임의의 참조변수 f에 익명 객체의 주소를 저장

타입 f = (int a, int b) -> a > b ? a : b; // 참조변수 f의 타입은 뭘로 해야 할까?
  • 참조변수 f의 타입

    • 참조형이니까 클래스 또는 인터페이스

    • 람다식과 동등한 메서드가 정의되어 있는 것이어야
      참조변수로 익명 객체(람다식)의 메서드를 호출할 수 있음

  • 예제) max()라는 메서드가 정의된 MyFunction인터페이스

    • MyFunction인터페이스를 구현한 익명 객체를 람다식으로 대체가 가능한 이유
      : 람다식도 실제로는 익명 객체이고, MyFunction인터페이스를 구현한 익명 객체의 메서드 max()와 람다식의 매개변수의 타입과 개수, 반환값이 일치하기 떄문
// MyFunction Interface에 max() Method 정의
Interface MyFunction {
    public abstract int max(int a, int b);
}

// 인터페이스를 구현한 익명클래스의 객체 생성
MyFunction f = new MyFunction() {
                     public int max(int a, int b) {
                         return a > b ? a : b;
                     }
               };
int big = f.max(5, 3); // 익명 객체의 메서드 호출

// MyFunction인터페이스에 정의된 max는 람다식 '(int a, int b) -> a > b ? a : b'와 메서드의 선언부가 일치
// 위 코드의 익명 객체를 람다식으로 대체
MyFunction f = (int a, int b) -> a > b ? a : b; // 익명 객체를 람다식으로 대체
int big = f.max(5, 3); // 익명 객체의 메서드 호출
  • 하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것

    • 기존의 자바의 규칙들을 어기지 않으면서도 자연스러움

    • 인터페이스를 통해 람다식을 다루기로 결정

    • 람다식을 다루기 위한 인터페이스를 “함수형 인터페이스(functional interface)”라고 부르기로 함

@FunctionalInterface
interface MyFunction {  // 함수형 인터페이스 MyFunction을 정의
    public abstract int max(int a, int b);
}

image