JAVA

[JAVA 기초] 함수형 인터페이스와 람다식(메서드를 일급객체로 사용하는 원리)

멋쟁휘개발자 2023. 10. 30. 23:13

람다식과 함수형인터페이스가 도입되기 전에는 함수형 언어를 흉내내기 위해,
익명 내부 클래스를 활용해서 익명함수 기능을 모방하는 방법을 활용했다.

그러나 JDK1.8부터 함수형 언어를 지원하기 위해,
람다식과 람다식을 다루기 위한 함수형 인터페이스가 도입 되었고
이때부터 자바는 함수형 언어를 포함하는 객체지향(OOP) 언어가 되었다.

 

 

 

 

먼저 함수와 메서드의 차이를 알아보자.

함수 vs 메서드
메서드는 클래스에 종속적, 함수는 독립적 (근본적으로 동일, 메서드는 OOP 개념 용어)

 

 

메서드를 일급 객체와 같이 클래스에 제한되지 않고 자유롭게 사용할 수 있도록,

Java 8에서 '람다식'과 '함수형 인터페이스'를 도입하였다.

'일급 객체' 란 사용할 때 다른 요소들과 아무런 차별이 없다는 것을 뜻하며, 아래 3가지 조건을 충족한 객체한다.
* 변수나 데이터에 담을 수 있어야 한다.
* 함수의 파라미터로 전달 할 수 있어야 한다.
* 함수의 리턴값으로 사용 할수 있어야 한다.

일급 객체는 어떠한 특정 언어에 국한되는 문법 단어가 아니다.
프로그래밍 언어론의 개념으로서, 자바나 자바스크립트 외에 다양한 언어가 이 일급 객체 개념을 가지고 있다.

발췌 : Inpa Dev


| 람다식

  • 람다식 == 익명 객체
  • 메서드의 이름, 반환타입 등이 없어도 되기 때문에 '익명 함수(anonymous function)'라고 불림
  • 익명함수를 간결하게 표현한 식
익명함수(이름 없는 함수)란,
- 일시적으로 한 번만 사용되고 버려져도 되는 객체일 경우에 사용 (메모리 관리 차원에서도 효율적이다.)
- 익명함수를 사용할 때는, 매번 new를 통해 객체를 생성해서 사용해야 하기 때문에 코드가 길어져 가독성도 떨어지고, 번거롭다.
//아래의 람다식은
(a, b) -> a > b ? a : b;

//아래의 익명 객체와 동일
new Object() {
	int max(int a, int b) {
    	return a > b ? a : b;
    }
}

//람다식을 사용하려면, 참조변수를 선언해야 함
Obejct obj = new Object() {
	int max(int a, int b) {
    	return a > b ? a : b;
    }
}

//하지만, Object 클래스에는 max라는 메서드가 없기 때문에 사용 불가
obj.max(3, 5);

 

 

 

 

그럼, 람다식을 사용하려면 어떻게 해야할까?

이때 사용하는 것이 '함수형 인터페이스(람다식을 다루기 위한 인터페이스)' 이다.

 

 

 

 

 

| 함수형 인터페이스(@FunctionalInterface)

  • 함수형 프로그래밍을 지원하기 위해서 도입
  • 추상 메서드 하나만 선언된 인터페이스
  • FunctionalInaterface Annotation 은 더보기 참고
더보기
Java SDK 8에서는 인터페이스가 Functional Interface 인지 확인
java에서 기본으로 제공이 아닌 직접 만든 Functional Interface에는 항상 @FunctionalInterface 애너테이션을 사용
(Effective Java Item 44 표준 함수형 인터페이스를 사용하라)

 

  1. 인터페이스가 람다용으로 설계된 것임을 알려주며
  2. 해당 인터페이스가 추상 메서드를 오직 하나만 가지고 있어야 컴파일이 되게 해주며
  3. 그 결과 유지 보수 과정에서 누가 실수로 메서드를 추가하지 못하게 막아주거나 함수형이어야 하는 인터페이스가 다른 인터페이스를 상속한 경우 미리 확인할 수 있다.

발췌 : https://colevelup.tistory.com/10

 

public class Ex01 {
    public static void main(String[] args) {

        // 람다식을 사용하기 위해서는 인터페이스의 추상메서드의 이름을 사용
        MyFunction myFunction = (a, b) -> a > b ? a : b;

        int result = myFunction.max(3, 5);

        System.out.println("result = " + result);
    }
}

@FunctionalInterface
interface MyFunction {
    
    /**
     * public abstract 생략가능 - interface 의 접근제어자는 기본이 public
     * 람다식을 다루기 위해, 인터페이스에 메서드 이름을 부여하고 사용한다.
     */
   int max(int a, int b); 
}

MyFunction인터페이스를 구현한 익명 객체를 람다식으로 대체가 가능한 이유는,
1. 람다식은 함수형 인터페이스와 마찬가지로 익명 객체이고,

2. 함수형 인터페이스의 메서드와 람다식의 매개변수의 타입과 개수 그리고 반환값이 일치하기 때문이다.

 

 

 

이제 아래와 같이 람다식과 함수형인터페이스를 통해, 메서드를 마치 변수처럼 주고 받을 수 있게 되었다.
(=일급객체로 다룰 수 있게 되었다.)

 

| 함수형 인터페이스와 람다식의 활용

 

1. 매개변수 함수형 인터페이스 타입 사용

void aMethod(MyFunction f) {
	f.myMethod();
}

@FunctionalInterface
interface MyFunction {
	void myMethod();
}

 

2. 반환타입으로 함수형 인터페이스 타입 사용

MyFunction mymethod() {
	MyFunction f =  () -> {};
	return f;
}

//simple version
MyFunction mymethod() {
	retrun () -> {};
}

 

 


| 익명함수, 람다식&함수형 인터페이스 도입 전, 후 비교

더보기

| 익명함수, 람다식&함수형 인터페이스 도입 전, 함수형 기능 모방 사용 예제

package org.example;

public class Anonymous {

    public static void main(String[] args) {
    
        MyFunction myFunction = new MyFunctionImpl();
        System.out.println("두 숫자의 합 = " + myFunction.plusNumber(1, 1));
    }
}

interface MyFunction {
    int plusNumber(int a, int b);
}

class MyFunctionImpl implements MyFunction {
    @Override
    public int plusNumber(int a, int b) {
        return a + b;
    }
}

 

| 익명함수만 활용한 함수형 기능 모방 사용 예제

package org.example;

public class Anonymous {

    public static void main(String[] args) {

	// new 를 통해 객체를 새로 만들고, 객체 안에 메서드를 정의
        MyFunction myFunction = new MyFunction() {

            @Override
            public int plusNumber(int a, int b) {
                return a + b;
            }
        };

        System.out.println("두 숫자의 합 = " + myFunction.plusNumber(1, 1));
    }
}


interface MyFunction {
    int plusNumber(int a, int b);
}

* 익명 함수에서는 클래스 이름이 없기 때문에 new를 통해 임시로 실체화하는 것

* 객체 생성이기 때문에 ; 을 꼭 붙여줘야 함

 

 

| 람다식과 함수형인터페이스를 활용한 예제

package org.example;

public class AnonymousToLambda {

    public static void main(String[] args) {

		// 람다식을 통해 간결하게 표현
		MyFunction2 myFunction = (a, b) -> a + b;

		System.out.println("두 숫자의 합 = " + myFunction.plusNumber(1, 1));
	}
}


@FunctionalInterface
interface MyFunction2 {
    int plusNumber(int a, int b);
}