TIL/JAVA

[TIL] 람다식 (with. 익명 클래스, 익명 함수)

Dream COM Ddulut 2024. 11. 27. 20:22

'람다식' 이란? (feat. 익명함수)

 

자바 8에서 도입된 기능으로 간결하게 익명 함수를 표현할 수 있는 방법.

익명함수는 이름이 없는 함수이다. 주로 일회성으로 사용된다. 

람다식은 익명 함수의 한 형태로 볼 수 있다.

 

람다식은 함수형 인터페이스의 경우에만 사용이 가능하다.

따라서 추상 메서드가 두 개 이상인 추상 클래스의 경우 람다식을 사용할 수 없고, 이때는 익명 클래스를 사용해야한다. 


 

람다식 문법

 

[기본 형식]

/*기본 형식 1*/
(parameters) -> expression

/*기본 형식 2*/
(parameters) -> { statements; }
expression, statements가 뭐지? 🤔

[expression]
숫자, 문자열, 변수, 연산자를 이용해 값을 생성하는 코드조각 (= 식)
int a = 5;  
int b = a + 3;  
boolean isTrue = (a > 3);


[statements]
• 조건문, 반복문, 선언문 등 하나의 작업을 수행하는 코드조각 (= 문)

int a = 5;  // 변수 선언문
a = a + 3;  // 대입문
if (a > 5) {  // if 조건문
    System.out.println("a는 5보다 큽니다.");  // 출력문
}

 

 

[예시]

1. 매개변수가 없는 경우

() -> System.out.println("Hello, world!");

 

2. 매개변수가 1개인 경우

x -> System.out.println(x);

 

3. 매개변수가 여러 개인 경우

(x, y) -> System.out.println(x + y);

 

4. 반환값이 있는 경우

(x, y) -> x + y; //return문 생략

람다식 사용 조건

 

람다식을 사용하려면 함수형 인터페이스가 필요하다.

함수형 인터페이스란? 🤔

• 딱 하나의 추상 메서드만 가지는 인터페이스
@FunctionalInterface 애노테이션을 사용하여 함수형 인터페이스를 명시할 수 있다.
• @FunctionalInterface를 사용하면 추상 메서드가 2개 이상 선언되는 것을 컴파일 에러를 통해 방지할 수 있다.
@FunctionalInterface
interface MyFunctionalInterface {
    void myMethod();
}​

 


 

'익명 클래스' 란? (feat. 지역 클래스)

 

익명 클래스는 이름이 없는 지역 클래스이다.

일반적으로 일회성으로 필요한 클래스, 인터페이스, 추상 클래스를 구현하기 위해 사용한다.

익명 클래스는 별도의 클래스 파일을 작성하지 않고도 인라인으로 클래스를 정의하고 인스턴스를 생성할 수 있다.

 

참고로 익명 클래스는 지역 클래스의 특별한 버전이다. 따라서 지역 클래스의 특징을 가지고 있다.

지역 클래스는 코드 블럭 안에서 클래스를 정의한다. 또한 내부 클래스이기 때문에 바깥 클래스의 인스턴스에 소속된다.

(자세한 내용은 중첩 클래스 - 내부 클래스 참고)

class Outer {
	public void process() {
    
        class Local {...} //지역 클래스, 바깥 클래스인 Outer클래스의 인스턴스에 소속된다.
        
        Local local = new Local();
    } 
}

 

 

'지역 클래스'의 특징 정리 (익명 클래스도 해당)

 

• static이 붙지 않는다.

 

바깥 클래스의 인스턴스에 소속된다.

  즉, 바깥 클래스의 인스턴스 정보를 알아야(=바깥 클래스의 인스턴스를 먼저 생성해야) 내부 클래스를 생성할 수 있다.
  따라서 static도 붙지 않는다.
  내부 클래스 생성 방법:   바깥 클래스 참조값 . new 내부 클래스( )  (※참조값 생략 가능)

public class InnerOuterMain {
    public static void main(String[] args) {
    
        InnerOuter outer = new InnerOuter(); //바깥 클래스 인스턴스 먼저 생성 
        InnerOuter.Inner inner = outer.new Inner(); //내부 클래스 인스턴스 생성
        
    }
}

 

'내부 클래스의 인스턴스'는 '바깥 클래스 인스턴스의 참조값'을 가지고 있다.
  따라서, 바깥 클래스의 인스턴스 멤버, 클래스 멤버, private 접근 제어자에 접근 가능하다.

 

지역 클래스는 접근 제어자를 사용할 수 없다.

 

인터페이스를 구현하거나, 부모 클래스를 상속할 수 있다.

  익명 클래스는 부모 클래스를 상속 받거나, 인터페이스를 꼭 구현해야한다. 

  즉, 익명 클래스는 상위 클래스 혹은 인터페이스를 필요로 한다.

 

지역 클래스는 힙 영역에 존재한다. (GC 전까지 생존)

  지역 클래스는 인스턴스 생성 시점에 필요한 바깥 클래스의 지역변수를 미리 복사 (=변수 캡처) 해둔다. (매개변수 포함)

  지역 클래스가 접근하는 지역 변수는 중간에 값이 변하면 안 된다. 따라서 final로 선언해야한다.

 


익명 클래스 선언 방법

 

지역 클래스의 이름을 생략하고, 클래스의 본문을 정의하면서 동시에 생성한다.

new 다음에 상속 받을 부모 클래스 혹은 인터페이스를 입력.

익명 클래스는 부모 클래스를 상속 받거나, 인터페이스를 꼭 구현해야한다. 

 

new Parent() {본문}

 

[예시]

1. 인터페이스를 사용한 익명 클래스 선언 

/*인터페이스*/
interface MyInterface {
    void doSomething();
}


/*익명 클래스 선언*/
public class Main {
    public static void main(String[] args) {
       
        MyInterface myObject = new MyInterface() { // 익명 클래스를 사용하여 MyInterface 구현
            @Override
            public void doSomething() {
                System.out.println("Doing something!");
            }
        };

    }
}