람다식 (1) - 기본 문법, 함수적 인터페이스

2023. 12. 7. 00:51java/java

 

람다식은 익명함수를 생성하기 위한 식으로 객체 지향 언어보다는 함수 지향 언어에 가깝다. 객체 지향 프로그래밍에 익숙한 개발자들은 다소 혼란스러울 수 있지만, 자바에서 람다식을 수용한 이유는 코드가 간결해지고, 컬렉션의 요소를 필터링하거나 매핑해서 원하는 결과를 쉽게 집계할 수 있기 때문이다.

 

람다식의 형태는 매개 변수를 가진 코드 블록이지만, 런타임 시에는 익명 구현 객체를 생성한다.

 

예를 들어 Runnable 인터페이스의 익명 구현 객체를 생성하는 전형적인 코드는 다음과 같다.

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        
    }
};

 

위 코드에서 익명 구현 객체를 람다식으로 표현하면 다음과 같다.

Runnable runnable = () -> {  };

 

람다식은 "(매개변수) -> {실행코드}" 형태로 작성되는데, 런타임 시에 인터페이스의 익명 구현 객체로 생성된다.

 

1. 람다식 기본 문법

함수적 스타일의 람다식을 작성하는 방법이다.

(타입 매개변수, ...) -> { 실행문; }

(타입 매개변수, ...) 는 { 실행문; } 을 실행하기 위해 필요한 값을 제공한다.(매개 변수 이름은 자유)

 

int 매개 변수 a의 값을 콘솔에 출력하는 람다식을 보자

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

 

매개 변수 타입은 런타임 시에 대입되는 값에 따라 컴파일러가 타입 추론을 할 수 있기 때문에 매개 변수 타입은 일반적으로 생략하고 작성한다.

(a) -> { System.out.pringln(a); }

 

매개 변수가 하나라면 소괄호도 생략 가능하다

a -> { System.out.pringln(a); }

 

int x, int y를 매개변수로 받고 그 합을 리턴하는 람다식을 작성해보자

(x, y) -> { return x + y }

 

만약 중괄호 안에 return문 밖에 없다면 중괄호와 return문을 사용하지 않아도 된다

(x, y) -> x + y

 

 

 

 

2. 타겟 타입과 함수적 인터페이스

2.1 타겟 타입

 

람다식의 형태는 매개 변수를 가진 코드 블록이기 대문에 자바 메소드와 비슷하게 보인다.

자바 메소드는 단독으로 선언할 수 없고 항상 클래스의 멤버로 선언된다.

람다식은 단순히 메소드를 선언하는 것이 아니라 이 메소드를 가지고 있는 객체를 생성한다.

 

인터페이스 변수 = 람다식;

 

위 람다식은 인터페이스 변수에 대입된다. 이 말은 람다식은 인터페이스의 익명 구현객체를 생성한다는 뜻이 된다.

인터페이스는 인스턴스화가 안되기 때문에 구현 클래스가 필요한데, 람다식이 이를 해결해준다.

람다식은 대입될 인터페이스의 종류에 따라 작성 방법이 달라지기 대문에 람다식이 대입될 인터페이스를 람다식의 타겟 타입이라고 한다.

 

2.2 함수적 인터페이스(@FunctionalInterface)

 

 모든 인터페이스를 람다식의 타겟 타입으로 사용할 수는 없다.

 람다식이 하나의 메소드를 정의하기 때문에 두 개 이상의 추상 메소드가 선언된 인터페이스는 람다식을 이용해서 구현 객체를 생성할 수 없다.

 

 하나의 추상 메소드가 선언된 인터페이스만이 람다식의 타겟 타입이 될 수 있는데, 이러한 인터페이스를 함수적 인터페이스라고 한다.

@FunctionalInterface
public interface TwoAbsctractInterface {
    void method1();
    void method2();
}

 

@FunctionalInterface 어노테이션은 선택사항이다. 이 어노테이션이 없어도 추상 메소드가 한 개라면 모두 함수적 인터페이스다. 하지만 @FunctionalInterface 어노테이션을 붙이지 않고 실수로 두 개 이상의 추상 메소드를 선언할 수도 있으니 붙여주는 것이 좋다. (붙였는데 두 개 이상 추상 메소드 붙이면 컴파일 에러)

 

2.3 매개변수와 리턴값이 없는 람다식

 

다음과 같이 매개 변수와 리턴값이 없는 추상 메소드를 가진 함수적 인터페이스가 있다고 가정해보자

@FunctionalInterface
public interface MyFunctionalInterface {
    void method();
}

 

이 인터페이스를 타겟 타입으로 갖는 람다식은 다음과 같은 형태로 작성해야 한다.

MyFunctionalInterface f1 = () -> { 실행문 }

 

람다식이 대입된 인터페이스의 참조 변수는 다음과 같이 method()를 호출할 수 있다.

f1.method();

 

public class InterfaceExample {

    public static void main(String[] args) {
        MyFunctionalInterface f1;
        
        f1 = () -> {
            String str = "method call1";
            System.out.println(str);
        };
        f1.method();
        
        f1 = () -> { System.out.println("method call2"); };
        f1.method();
        
        f1 = () -> System.out.println("method call3"); //실행문이 하나라면 중괄호 생략 가능
        f1.method();
        
    }
}

 

2.4 매개변수가 있는 람다식

 

다음과 같이 매개 변수가 있고 리턴값이 없는 추상 메소드를 가진 함수적 인터페이스가 있다.

@FunctionalInterface
public interface MyFunctionalInterface {
    void method(int x);
}

 

이 인터페이스를 타겟 타입으로 갖는 람다식은 다음과 같은 형태로 작성해야 한다.

MyFunctionalInterface f1 = (x) -> { 실행문 }
MyFunctionalInterface f1 =  x  -> { 실행문 }

 

람다식이 대입된 인터페이스의 참조 변수는 다음과 같이 method()를 호출할 수 있다.

public class InterfaceExample {

    public static void main(String[] args) {
        MyFunctionalInterface f1;
        
        f1 = (x) -> {
            int result = x * 10;
            System.out.println(result);
        };
        f1.method(5);
        
        f1 = (x) -> { System.out.println(x * 10); };
        f1.method(6);
        
        f1 = x -> System.out.println(x * 10);
        f1.method(7);
    }
}

 

2.5 리턴값이 있는 람다식

다음과 같이 매개 변수가 있고 리턴값이 있는 추상메소드를 가진 함수적 인터페이스가 있다.

@FunctionalInterface
public interface MyFunctionalInterface {
    int method(int x, int y);
}

 

이 인터페이스를 타겟 타입으로 갖는 람다식은 다음과 같은 형태로 작성해야 한다.

 

MyFunctionalInterface f1 = (x, y) -> { ...; return 값 }

만약 중괄호 { } 에 return문만 있고, return문 뒤에 연산식이나 메소드 호출이 오는 경우라면
다음과 같이 작성할 수 있다.

MyFunctionalInterface f1 = (x, y) -> x + y;
MyFunctionalInterface f1 = (x, y) -> sum(x, y);

 

람다식이 대입된 인터페이스의 참조 변수는 다음과 같이 method()를 호출할 수 있다.

public class InterfaceExample {

    public static void main(String[] args) {
        MyFunctionalInterface f1;

        f1 = (x, y) -> {
            int result = x + y;
            return result;
        };
        System.out.println(f1.method(1, 2));
        
        f1 = (x, y) -> { return x + y };
        System.out.println(f1.method(3, 4));
        
        f1 = (x, y) -> x + y;
        System.out.println(f1.method(5, 6));
        
        f1 = (x, y) -> sum(x, y);
        System.out.println(f1.method(7, 8));
    }
    
    public static int sum(int x, int y) {
        return x + y;
    }
 }

'java > java' 카테고리의 다른 글

람다식(3) - 표준 API의 함수적 인터페이스  (0) 2023.12.09
람다식(2) - 클래스 멤버와 로컬 변수 사용  (0) 2023.12.08
Generic  (1) 2023.12.04
중첩 클래스와 중첩 인터페이스란?  (0) 2023.11.29
어노테이션  (2) 2023.11.26