Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발자 도전기

[JAVA] 람다식(Lambda Expressions) 본문

개발공부/JAVA

[JAVA] 람다식(Lambda Expressions)

jnnjnn 2024. 3. 13. 17:47

자바는 객체 지향 프로그래밍을 기반으로 하고 있지만 함수형 프로그래밍을 위한 구현체들을 만들어두었다. StreamAPI나 람다식 등이 이에 해당된다.

 

람다식이란?

 

함수형 인터페이스(Functional Interface)의 익명 구현 객체를 간단하게 표현하는 방법을 말한다.

함수형 인터페이스란 단 하나의 추상 메소드만 가지고 있는 인터페이스이다. @FunctionalInterface 어노테이션을 인터페이스 선언부 위에 붙여서 인터페이스가 함수형 인터페이스인지 확인할 수 있다.

(매개변수) -> { 처리내용 };
(x, y) -> { 처리내용 };

 

람다식은 인터페이스의 익명 구현 객체이므로 인터페이스 타입의 매개변수에 대입될 수 있고 주로 Stream 연산에 활용된다.

 

 

람다식으로 표현하기

익명 구현 객체는 다음과 같이 람다식으로 바꾸어서 표현할 수 있다

public class C01Lambda {
    public static void main(String[] args) {
        
        // 인터페이스의 익명 구현 객체
        C01MyInterface obj1 = new C01MyInterface() {
            @Override
            public void method() {
                System.out.println("C01Lambda.method");
            }
        };
        
        // 람다식으로 변환
        C01MyInterface obj2 = () -> System.out.println("C01Lambda.main");
    }
}

interface C01MyInterface {
    void method();
}

 

 

여러가지 람다식

매개변수가 없는 람다식은 다음과 같이 작성할 수 있다. 실행문이 두 개 이상일 경우에는 중괄호를 생략할 수 없고, 하나일 경우에만 생략할 수 있다.

() -> {
	실행문;
    	실행문;
}

() -> 실행문

() -> System.out.println("C04Lambda.main");

 

매개변수가 있는 람다식은 다음과 같이 작성할 수 있다. 매개변수를 선언할 때 타입은 생략가능하다.

매개변수가 하나일 경우 괄호를 생략할 수 있다. 이때는 타입 또는 var를 붙일 수 없다.

 

매개변수 -> {
	실행문;
    	실행문;
    }
    
매개변수 -> 실행문

(x, y) -> System.out.println("C04Lambda.main");

 

함수형 인터페이스의 추상 메소드에 리턴값이 있을 경우 람다식은 다음과 같이 작성할 수 있다.

return 값이 하나만 있을 경우에는 중괄호와 함께 return 키워드를 생략할 수 있다. 리턴값은 연산식 또는 리턴값 있는 메소드 호출로 대체할 수 있다.

(매개변수) -> {
	실행문;
    return 값;
    }

(매개변수) -> 값

 

다음은 매개변수가 있고 리턴값이 있는 람다식을 사용하는 예제이다.

// 함수형 인터페이스
@FunctionalInterface
public interface Function {
    double func(double x, double y);
}

public class FunctionExample {
    public static void main(String[] args) {
        // 람다식을 매개변수로 제공
        action((x, y) -> x + y);
        action((x, y) -> x / y);
    }

    static void action(Function function) {
        double result = function.func(10, 4);
        System.out.println(result);
    }
}

 

 

메소드 참조

메소드 참조는 메소드를 참조해서 매개변수의 정보 및 리턴 타입을 알아내 람다식에서 불필요한 매개변수를 제거하는 것을 목적으로 한다. 즉, 매개변수나 리턴 타입을 생략한 람다식이다. 예를 들어 두 개의 값을 받아 큰 수를 리턴하는 Math 클래스의 max() 정적 메소드를 호출하는 람다식은 단순히 두 개의 값을 Math.max() 메소드의 매개값으로 전달하는 역할만 하기 때문에 다음과 같이 정리할 수 있다.

 

// 람다식
(left, right) -> Math.max(left,right);

// 메소드 참조 람다식
Math :: max;

 

정적 메소드와 인스턴스 메소드 참조

정적 메소드를 참조할 경우에는 클래스 이름 뒤에 :: 기호를 붙이고 정적 메소드 이름을 기술한다.

인스턴스 메소드일 경우에는 먼저 객체를 생성해야 사용할 수 있으므로 먼저 객체를 생성한 다음 참조 변수 뒤에 :: 기호를 붙이고 인스턴스 메소드 이름을 기술한다.

 

// 정적 메소드 참조
클래스 :: 메소드

// 인스턴스 메소드 참조
참조변수 :: 메소드

 

예제

public class C18MethodReference {
    public static void main(String[] args) {
        Computer com = new Computer();
        com.action(Lambda::staticMethod); // 14

        Lambda lam = new Lambda();
        com.action(lam::instanceMethod); // 6
    }
}

class Computer {
    public void action(Function function) {
        int result = function.method(10, 4);
        System.out.println(result);
    }
}

class Lambda {
    public static int staticMethod(int a, int b) {
        return a + b;
    }

    public int instanceMethod(int a, int b) {
        return a - b;
    }
}

@FunctionalInterface
interface Function {
    public int method(int a, int b);
}

 

 

매개변수의 메소드 참조

람다식에서 제공되는 a 매개변수의 메소드를 호출해서 b 매개변수를 매개값으로 사용하는 경우도 있다.

이것을 메소드 참조로 표현하면 a의 클래스 이름 뒤에 :: 기호를 붙이고 메소드 이름을 기술한다. 

(a, b) -> a.instanceMethod(b);

클래스 :: instanceMethod;

 

예제

public class C14InstanceMethodReference {
    public static void main(String[] args) {
        C14OtherClass obj = new C14OtherClass();
        C14MyInterface lam1 = (a, b) -> a.someMethod(b);
        C14MyInterface lam2 = C14OtherClass::someMethod;

    }
}

class C14OtherClass {
    void someMethod(int x) {
        System.out.println("C14OtherClass.someMethod");
    }
}

@FunctionalInterface
interface C14MyInterface {
    void method(C14OtherClass obj, int x);
}

 

 

생성자 참조

생성자를 참조한다는 것은 객체를 생성하는 것을 의미한다. 람다식이 단순히 객체를 생성하고 리터나도록 구성된다면 람다식을 생성자 참조로 대치할 수 있다.

 

(a, b) -> return new 클래스(a,b);

클래스 :: new

 

생성자가 오버로딩되어 여러 개가 있을 경우, 컴파일러는 함수형 인터페이스의 추상 메소드와 동일한 매개변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다. 만약 해당 생성자가 존재하지 않으면 컴파일 오류가 발생한다.

 

public class C17ConstructorReference {
    public static void main(String[] args) {
        C17MyInterface1 obj1 = C17MyClass::new;
        C17MyInterface1 obj2 = () -> new C17MyClass();
        C17MyInterface2 obj3 = C17MyClass::new;
        C17MyInterface2 obj4 = (s, i) -> new C17MyClass(s, i);
    }
}

class C17MyClass {
    C17MyClass() {
    }

    C17MyClass(String s, int i) {
    }
}

@FunctionalInterface
interface C17MyInterface1 {
    C17MyClass Method();
}

@FunctionalInterface
interface C17MyInterface2 {
    C17MyClass method(String s, int i);
}

 

 

'개발공부 > JAVA' 카테고리의 다른 글

[JAVA] 컬렉션 프레임워크  (0) 2024.03.18
[JAVA] 정규표현식(regex)  (0) 2024.03.15
[JAVA] 익명 객체  (0) 2024.03.12
[JAVA] 중첩 선언(중첩 클래스, 중첩 인터페이스)  (0) 2024.03.12
[JAVA] 인터페이스(interface)  (0) 2024.03.11