3 minute read

람다식

  • Lambda Expressions
  • 자바에서 함수적 프로그래밍을 위해 자바 8부터 지원
  • 익명함수 생성을 위한 식. 객체 지향이 아닌 함수 지향에 가까움
  • 매개변수를 갖는 함수와 같은 코드 블록이지만 런타임 시에는 인터페이스의 익명 구현 객체를 생성
  • 컬렉션의 요소를 필터링하거나 매핑하여 원하는 결과를 쉽게 도출

람다식 장단점

  • 장점
    • 간결한 코드 작성
    • 가독성이 향상
    • 함수를 만드는 과정없이 한번에 처리할 수 있기에 코딩하는 시간이 감소
    • 병렬 프로그래밍이 용이
  • 단점
    • 람다식을를 사용하면서 만드는 무명함수는 재사용 불가능
    • 디버깅이 어려움
    • 재귀로 만들 경우 부적합

람다식 사용법

() -> {
    실행문;
};

([타입 매개변수, ...]) -> {
    실행문;
    ...
};
  • [타입 매개변수, …] : 중괄호 블록{} 실행에 필요한 값을 제공(매개변수)
  • -> : 매개변수를 이용하여 중괄호 블록을 실행

람다식 사용 예시

(int a) -> { System.out.println(a); };

// 매개변수가 하나일 경우 타입 생략 가능(런타임 시(실행) 자동으로 타입 인식)
(a) -> { System.out.println(a); };

// 매개변수가 하나일 경우 괄호, 타입 생략 가능 / 실행문이 하나일 경우 중괄호 생략 가능
a -> System.out.println(a);

// 결과값 리턴 시
(a, b) -> { return a + b; };

// 리턴문이 하나일 경우 ruturn, 중괄호 생략 가능
(a, b) -> a + b;

// 매개변수가 없을 경우에는 빈괄호 사용
() -> { 실행문; };

람다식을 이용한 스레드 생성

public void lambda() {
    Thread thread;
    
    thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("익명 구현 객체를 사용한 Thread 생성");
        }
    });
    thread.start();
    
    // lambda expressions
    thread = new Thread(() -> {
        System.out.println("람다식을 이용한 Thread 생성");
    });
    thread.start();
}

코드가 간결해진 건 알겠다. 이거 눈에 익히려면 스레드부터 확실히 해야할듯.
이게 가능한 이유는 run() 메서드가 매개값도 없고, 반환값도 없기 때문..


target type

  • 람다식이 대입될 인터페이스를 타겟 타입이라고 부름
    • 람다식으로 사용한 메서드를 갖고 있는 객체를 생성
    • 인터페이스 타입의 변수에 대입되고, 인터페이스의 익명 구현 객체를 생성
    • 대입될 인터페이스의 종류에 따라 작성법 상이

functional interface

  • 하나의 메서드만 갖고 있는 인터페이스
  • @FunctionalInterface
    • 컴파일러가 체크
    • annotation은 생략이 가능하지만 생략 시, 두 개 이상의 추상 메서드를 선언해도 오류가 발생하지 않기 때문에 사용 권장

Alt text

이 이미지를 보면
@FunctionalInterface 어노테이션이 있을 때와 없을 때의 차이를 확인할 수 있다.
위/아래 모두 두 개의 메서드를 갖고 있는데 어노테이션에 따라 에러 체크의 여부가 달라진다.
두 개의 추상 메서드를 선언하니 바로 빨간줄로 표시해준다.
어제 내가 짠 코드도 기억이 안 나니.. 명시적으로 꼭 붙여주는 걸로 하자.


매개변수와 반환값이 없는 람다식

Alt text

인터페이스 자체에 매개변수와 반환값이 없기 때문에
람다식을 사용할 때도 동일하게 맞춰줘야하는 게 포인트!

fi.method(); : 람다식으로 사용한 메서드를 갖고 있는 객체를 생성하기 때문에 이렇게 작성

// 람다식 사용
A_FuncInterface fi = () -> {
    System.out.println("매개변수와 반환값이 없는 람다식 ");
};
fi.method();

// 실행문이 하나일 경우 중괄호 생략 가능
A_FuncInterface fi = () -> System.out.println("매개변수와 반환값이 없는 람다식 ");
fi.method();

// -------------------------------------------------------------------------------

// 람다식 사용 X
fi = new A_FuncInterface() {
    @Override
    public void method() {
        System.out.println("매개변수와 반환값이 없는 람다식 ");
    }
};
fi.method();

람다식을 사용한 코드와, 람다식을 사용하지 않고 Interface를 구현할 경우의 코드 비교.

Alt text


매개변수가 있고 반환값이 없는 람다식

// Interface
@FunctionalInterface
public interface B_FuncInterface {
	// 매개변수가 있고 반환값이 없는 추상 메서드
	public void method(int a);
}

int a 라는 매개변수가 있다. 리턴값은 없음.

// Interface 구현
B_FuncInterface fi = (int a) -> {
    System.out.println(a);
};
fi.method(5);
    
// 매개변수가 하나일 경우 소괄호 생략 가능
// 실행문이 하나일 경우 중괄호 생략 가능
B_FuncInterface fi;
fi = a -> System.out.println(a);
fi.method(10);

위 인터페이스를 람다식을 사용하여 구현한 코드이다.
인터페이스에 int a 매개 변수가 있기 때문에 동일하게 작성했다.
반환값은 없기 때문에 마찬가지로 람다식 코드에도 없도록 맞춰준다.


매개변수와 반환값이 있는 람다식

// Interface
@FunctionalInterface
public interface C_FuncInterface {
	// 매개변수와 반환값이 있는 추상 메서드
	public int method(int a, int b);
}
// 정수형으로 반환하게 되어있음(int)
C_FuncInterface fi = (int x, int y) -> {
    System.out.println(x + y);
    return x + y;
};
fi.method(10, 20); // 30 출력


// ------------------------------------------------------------------------
int result = 0;
C_FuncInterface fi;

// 매개변수 자료형 생략 가능
// return문이 하나일 경우 return 키워드, 중괄호 생략 가능
fi = (x, y) -> x + y;
result = fi.method(10, 20);

System.out.println(result); // 30 출력

줄이다 보니 라인이 더 길어진 거 같긴 하지만 확장성은 있어보임.


람다식에서 변수 사용

  • 람다식에서 this는 람다식을 실행하는 객체를 의미

Alt text

위 코드에서 보면 method 안에 20이 대입된 num이라는 변수가 있는데 100을 출력한다.
람다식을 실행하는 객체를 가르킨다는 의미이다.

A_FuncInterface fi = () -> {
    this.num = 200; // 값 변경 가능

    System.out.println(this.num); // 출력도 가능
};

-

private int num = 100; // 필드(인스턴스 변수)

// 로컬 변수 사용
public void method4(int arg) {
    int num = 20; // 로컬 변수 사용

    // num = 200; // 이렇게 하면 error. 값 재할당 불가
    fi = () -> {
        System.out.println(num + ", " + arg);
    };
    
    fi.method();
}
  • 람다식에서 로컬변수(매개변수) 사용 가능
  • 인스턴스 변수는 제약 없이 사용 가능
  • 로컬 변수(매개변수) 사용 시에는 변수가 final 혹은 effectively final 이어야함
    • 읽는 것만 허용
    • 변경 불가
    • effectively final : 초기화 이후 값이 변경되지 않은 변수

Categories:

Updated: