JAVA/Java

[Java] 익명 클래스(Anonymous Class)와 람다식(Lambda Expression)

민트맛녹차 2022. 9. 13. 13:06

익명클래스(Anonymous Class)

interface Printable {
    void print();
}

class Printer implements Printable {
    public void print(String s) {
        System.out.println(s);
    }
}

class Example {
    public static void main(String[] args) {
    	Printable prn = new Printer();
        prn.print("hi");
    }
}

위 코드에서 Printer 클래스를 보자.  Printer 클래스의 정의와 Printer 인스턴스의 생성이 분리되어 있다. 하지만 '익명 클래스'의 형태를 사용하면 클래스의 정의와 인스턴스 생성을 하나로 묶을 수 있다.

 

class Example {
    public static void main(String[] args) {
    	Printable prn = new Printable() {
            public void print(String s) {
                System.out.println(s);
            }
        }
        prn.print("hi");
    }
}

위으 코드를 보면 이전 클래스의 정의와 인스턴스의 생성이 한번에 이루어진다. 익명 클래스는 이와 같이 일시적으로 사용되는 클래스를 만드는 기능을 제공해 코드를 줄이는 이점을 제공한다.

익명 클래스는 일시적으로 한번만 사용되는 객체를 만들거나, 재사용성이 없고 확장성을 확용하는 것이 유지보수에 불리한 클래스를 만들 때 사용한다.

 

 

람다식(Lamda Expression)

그렇다면 람다식은 무엇일까?

람다식은 함수형 프로그래밍에서 사용되는 문법으로 익명 클래스와 비슷하게 익명 클래스를 생성한다.

class Example {
    public static void main(String[] args) {
    	Printable prn = (s) -> { System.out.println(s); }
        prn.print("hi");
    }
}

위의 코드는 익명 클래스를 사용한 예제를 람다식으로 바꾼 코드이다. 람다식을 사용하면 이처럼 코드가 더 간결해진다.

람다는 기능 하나를 정의해서 전달해야 하는 상황일때 사용한다.

 

람다식은 매개변수와 반환형의 유무에 따라 식의 표현이 달라진다.

/* 매개변수 있고 반환 없는 람다 */
interface Printable {
    void print(String s);
}

Printalbe p1 = (String s) -> { System.out.println(s); }; // 줄임 없음
Printalbe p2 = (String s) -> System.out.println(s); // 중괄호 생략
Printalbe p3 = (s) -> System.out.println(s); // 매개변수 형 생략
Printalbe p4 = s -> System.out.println(s); // 매개변수 소괄호 생략

메서드의 몸체가 하나의 문장으로 이루어져 있다면 위와 같이 중괄호의 생략이 가능하다.

매개변수 형은 컴파일러가 유추할 수 있으므로 생략 가능하다. 그리고 매개변수가 하나일 경우에는 소괄호도 생략가능하지만 둘 이상인 경우에는 생략이 불가능하다.

/* 매개변수 있고 반환하는 람다*/
interface Calculate {
    int cal(int a, int b);
}

Calculate c1 = (a, b) -> { return a + b; };
Calculate c2 = (a, b) -> a + b;

메서드 몸체의 내용이 return 문이라면 중괄호의 생략이 불가능 하지만 return 문이 메서드 몸체를 이루는 유일한 문장이면 c2 처럼 작성할 수 있다.

/* 매개변수가 없는 람다 */
interface Generator {
    int rand();
}

Generator gen = () -> {
    Random rand = new Random();
    return rand.nextInt(50);
}

매개변수가 없는 경우는 빈 소괄호를 사용해야 하고, 메서드 몸체가 둘 이상으로 이루어져 있다면 중괄호로 반드시 감싸야한다.

 

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

람다식을 사용하기 위해서는 구현할 인터페이스가 필요하다. 이때 구현할 인터페이스에는 추상 메서드가 단 하나만 존재해야한다. 이러한 인터페이스를 함수형 인터페이스(Functional Interface)라 한다.     

@FunctionalInterface
interface Calculate {
    int cal(int a, int b);
}


@FunctionalInterface
interface Calculate2 {
    int cal(int a, int b);
    default int add(int a, int b) { return a + b; }
    static int sub(int a, int b) { return a - b; }
}

@FunctionalInterface를 사용한 인터페이스에 둘 이상의 추상 메서드가 존재하면 컴파일 에러가 발생한다. 하나 이상의 추상 메서드가 존재한다면 함수형 인터페이스가 아니기 때문이다. 

하지만 static, default 선언이 붙은 메서드의 정의는 함수형 인터페이스 정의에 아무런 영향을 미치지 않으므로 Calculate2는 함수형 인터페이스이다.

 

제네릭으로 정의된 함수형 인터페이스를 대상으로 하는 람다식을 사용할 수 있다.

@FunctionalInterface
interface Calculate<T> {
    T cal(T a, T b);
}

Calculate<Integer> ci = (a, b) -> a + b;
Calculate<Double> ci = (a, b) -> a + b;

두 문장은 람다식은 동일하나 참조변수의 자료형이 다르므로, 다른 인스턴스의 생성으로 이어진다. 

                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

참조
윤성우의 열혈 JAVA 프로그래밍
https://limkydev.tistory.com/226
https://juyoung-1008.tistory.com/48