본문 바로가기

프로그래밍 공부/Java

Java - Java 8 : Lambda

람다 표현식(Lambda expression)

람다 표현식은 JavaScript의 '화살표 함수(또는 람다식)'과 같다.

1
2
3
4
5
6
7
// 일반적인 메서드
int min(int x, int y) {
    return x < y ? x : y;
}
 
// 람다(Lambda) 표현식
(x, y) -> x < y ? x : y;
cs

위의 코드처럼 메서드를 람다 표현식으로 표현하면 클래스를 작성하고 객체를 생성하지 않아도 메서드를 사용할 수 있다. 그런데 Java에서는 클래스의 선언과 동시에 객체를 생성하므로, 단 하나의 객체만을 생성할 수 있는 클래스를 익명 클래스라고 한다.

따라서 람다 표현식은 익명 클래스와 같다고 할 수 있다.
1
2
3
4
5
6
7
8
9
// 람다(Lambda) 표현식
(x, y) -> x < y ? x : y;
 
// 익명(Anonymouse) 클래스
new Object(){
    int min(int x, int y){
        return x < y ? x : y;
    }
}
cs

이런 람다 표현식은 메서드의 매개변수로 전달될 수도 있고, 메서드의 결과값으로 리턴될 수도 있다. 

따라서 람다 표현식을 사용하면 기존의 불필요한 코드를 줄이고 코드의 가독성이 향상된다, Java SE 8부터는 이러한 람다 표현식을 사용해 Java에서도 함수형 프로그래밍을 할 수 있다.

람다 표현식 작성

Java에서는 화살표(->) 기호를 사용해 람다 표현식을 작성할 수 있다.

또한 람다 표현식을 작성할 때 유의해야 할 사항이 있다.

1. 매개변수의 타입을 추론할 수 있는 경우 타입을 생략할 수 있다.
2. 매개변수가 하나인 경우에는 괄호를 생략할 수 있다.
3. 함수의 몸체가 하나의 명령문만으로 이루어진 경우에 중괄호를 생략할 수 있다.(이때 세미콜론은 붙이지 않는다.)
4. 함수의 몸체가 하나의 return문으로만 이루어진 경우에는 중괄호를 생략할 수 있다.
5. return 문 대신 표현식을 사용할 수 있고, 이때 리턴값은 표현식의 결과값이 된다.(이때 세미콜론은 붙이지 않는다.)

1
2
3
4
5
6
7
8
9
10
11
12
// 람다(Lambda) 표현식 작성
( 매개변수목록 ) -> { 함수몸체 }
 
new Thread(new Runnable(){
    public void run(){
        System.out.println("전통적인 방식의 일회용 스레드 생성");
    }
}).start();
 
new Thread(() -> {
    System.out.println("람다 표현식을 사용한 일회용 스레드 생성");
}).start();
cs

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

람다 표현식을 사용할 때는 람다 표현식을 저장하기 위해 참조 변수 타입을 결정해야 한다. 람다 표현식을 하나의 변수에 대입할 때 사용되는 참조 변수의 타입을 함수형 인터페이스라고 부른다.

함수형 인터페이스는 추상 클래스와는 달리 단 하나의 추상 메서드만을 가져야하고 어노테이션(Annotation)을 사용해 함수형 인터페이스임을 명시 할 수 있다.
1
2
3
4
5
// 함수형 인터페이스(Functional interface)
참조변수의타입 참조변수명 = 람다표현식
 
// 어노테이션(Annotation)
@FunctionalInterface
cs

어노테이션을 인터페이스의 선언 앞에 붙이게되면, 컴파일러는 해당 인터페이스를 함수형 인터페이스로 인식하게 된다. Java 컴파일러는 이렇게 명시된 인터페이스에 두 개 이상의 메서드가 선언되면 오류를 발생시킨다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 어노테이션(Annotation)
@FunctionalInterface
// 함수형 인터페이스의 선언
interface Calc { 
    public int min(int x, int y);
}
 
public class Main {
    public static void main(String[] args){
        // 추상 메소드의 구현
        Calc minNum = (x, y) -> x < y ? x : y; 
        // 함수형 인터페이스의 사용
        System.out.println(minNum.min(34));  
    }
}
cs

 

Java는 java.util.function 패키지를 통해 여러 상황에서 사용할 수 있는 다양한 함수형 인터페이스를 미리 정의하고 제공한다.

메서드 참조(Method reference)

메서드 참조는 람다 표현식이 단 하나의 메서드만을 호출하는 경우에 해당 람다 표현식에서 불필요한 매개변수를 제거하고 사용할 수 있게 해준다. 메서드 참조를 사용하면 불필요한 매개변수를 제거하고 :: 기호를 사용해 표현할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 메서드 참조(Method reference)
클래스명::메서드명
 
// 또는
참조변수명::메서드명
 
// 기존
(base, exponent) -> Math.pow(base, exponent);
 
// 람다 표현식
Math::pow;
 
// 참조 변수명을 통한 인스턴스의 메서드 참조
MyClass obj = new MyClass;
Function<String, Boolean> func = (a) -> obj.equals(a);    // 람다 표현식
Function<String, Boolean> func = obj::equals(a);        // 메서드 참조
 
// 람다 표현식과 메서드 참조를 비교
DoubleUnaryOperator oper;
 
oper = (n) -> Math.abs(n);    // 람다 표현식
System.out.println(oper.applyAsDouble(-5));
 
oper = Math::abs;            // 메서드 참조
System.out.println(oper.applyAsDouble(-5));
cs
람다 표현식과 메서드 참조를 비교하는 코드에서 DoubleUnaryOperator 인터페이스는 한 개의 double 형 매개변수를 전달받아 한 개의 double 형 값을 리턴하는 java.util.function 패키지에서 제공하는 함수형 인터페이스이다.

생성자 참조

생성자를 호출하는 람다 표현식도 앞서 살펴본 메서드 참조를 이용할 수 있다. 단순히 객체를 생성하고 리턴하는 람다 표현식은 생성자 참조로 변환할 수 있는 것이다.

1
2
3
4
5
6
7
8
9
// 단순히 객체를 생성하고 리턴하는 람다 표현식
(a) -> { return new Object(a); }
 
// 위의 코드를 생성자 참조를 사용해 표현
Object::new;
 
// 배열을 생성할 때 생성자 참조를 사용
Function<Integer, double[]> func1 = a -> new double[a];    // 람다 표현식
Function<Integer, double[]> func2 = double[]::new;        // 생성자 
cs

두 번째 코드에서 주의할 점은 해당 생성자가 존재하지 않으면 컴파일시에 오류가 발생한다.

 

'프로그래밍 공부 > Java' 카테고리의 다른 글

Java - Java 8 : 스트림 API(Steam API)  (0) 2020.02.27
Java - 컬렉션 프레임워크(Collection framework)  (0) 2020.02.26
Java - 스레드(Thread)  (0) 2020.02.25
Java - 예외 처리  (0) 2020.02.24
Java - 제네릭(Generic)  (0) 2020.02.24