JAVA/BASIC

람다식과 인터페이스 익명구현 객체

유혁스쿨 2022. 10. 27. 14:31
728x90
반응형

람다식

 자바는 객체 지향 프로그래밍이 소프트웨어 개발의 주요 패러다임이었던 1990년대에 디자인 되었다.

객체 지향 프로그래밍이 나오기 오래 전부터 Lisp 또는 Scheme와 같은 함수적 프로그래밍 언어들이 있었다.

하지만 학계를 제외하고는 현업에서는 큰 호응을 얻지 못하였다.

최근 함수적 프로그래밍이 다시 부각되고 있는데, 병렬 처리와 이벤트 지향 프로그래밍에 적합하기 때문이다.

따라서 객체 지향 프로그래밍과 함수적 프로그래밍을 혼합함으로써 더욱 효율적인 프로그래밍이 될 수 있도록 프로그램 개발 언어가 변하고 있다.

 자바는 함수적 프로그래밍을 위해 자바 8부터 람다식(Lambda Expressions)을 지원하면서 기존의 코드 패턴이 많이 달라졌다.

람다식은 수학자 알론조 처치(Alonzo Church)가 발표한 람다 계산법에서 사용된 식으로, 이를 제자 존 매카시(John McCarthy)가 프로그래밍 언어에 도입했다.

 람다식은 익명함수(annonymous function)를 생성하기 위한 식으로 객체 지향 언어보다는 함수 지향 언어에 가깝다.

자바에서 람다식을 수용한 이유는 자바 코드가 매우 간결해지고, 컬렉션의 요소를 필터링하거나 매핑해서 원하는 결과를 쉽게 집계할 수 있기 때문이다.

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

 

interface MyFunction {
    public abstract int max(int a, int b);
    public abstract int min(int a, int b);
}

public class Lamda_Ex implements MyFunction{
    public static void main(String[] args) {

        @Override
        public int max(int a, int b) {
            return 0;
        }

        @Override
        public int min(int a, int b) {
            return 0;
        }
    }
}

일반적인 인터페이스 추상메서드 구현 코드이다.

 

 

interface MyFunction {
    public abstract int max(int a, int b);
    public abstract int min(int a, int b);
}

public class Lamda_Ex{
    public static void main(String[] args) {
        /*익명 구현 객체는 Implements를 하지 않고 바로 원하는 인터페이스의 메소드를 구현가능.*/
        MyFunction f = new MyFunction() {
            @Override
            public int max(int a, int b) {
                return 0;
            }

            @Override
            public int min(int a, int b) {
                return 0;
            }
        };
    }
}

익명 구현 객체로 인터페이스의 추상메서드를 따로 구현하지않고, 익명 구현 객체를 통해 구현한다.

 

람다식도 비슷하게 구현한다.

interface MyFunction {
    public abstract int max(int a, int b);
}

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

        /*익명 구현 객체는 Implements를 하지 않고 바로 원하는 인터페이스의 메소드를 구현가능.*/
        MyFunction f = (int a, int b) -> {return a > b ? a : b;};
    }
}

 

람다식은 함수형 인터페이스 타입의 참조변수로 참조할 수 있다.

여기서, 함수형 인터페이스란 단 하나의 추상메소드만 선언된 인터페이스이다.

 

만약, 위 코드에서 추상메서드가 두개라면 오류가 발생한다.

interface MyFunction {
    public abstract int max(int a, int b);
    public abstract int min(int a, int b);
}

public class Lamda_Ex{
    public static void main(String[] args) {
        MyFunction f = (int a, int b) -> {return a > b ? a : b;};
    }
}

이를 방지하는 방법으로 @FunctionalInterface 어노테이션이 있다.

타겟타입 인터페이스에 @FunctionalInterface 어노테이션을 선언해주면 컴파일러가 두개 이상의 메소드가 선언되지 않도록 체킹해준다.

만약 두개 이상의 추상메소드가 선언되면 컴파일 오류를 발생시킨다.

@FunctionalInterface 어노테이션은 선택사항이다.

해당 어노테이션이 없더라도 하나의 추상메소드만 가지고 있다면 모두 함수적 인터페이스이기 때문이다.

실수로 두 개 이상의 추상 메소드를 선언하는 것을 방지하고자 한다면 붙여주는 것이 좋다.

interface MyFunction {
    public abstract int max(int a, int b);
    public abstract int min(int a, int b);
}

public class Lamda_Ex{

    /* 
    아래와 같이 선언하면 오류가난다. 
    인터페이스 구현은 선언된 추상메소드를 모두 오버라이딩 해야한다.
    (구현하지 않을 메소드가 있다면 기본메소드 형태로 오버라이딩 해줘야 한다.)
    */
    MyFunction f = new MyFunction() {
        @Override
        public int max(int a, int b) {
            return 0;
        }
    };
}

인터페이스 구현 추상메서드 오버라이딩 혹은 익명구현객체 문법에서는 위 코드의 주석 내용처럼 추상메소드를 모두 오버라이딩 해줘야만 한다. (위처럼 하나만 오버라이딩하면 오류남. 일반적인 구현방식에서도 마찬가지입니다.)

 

 

따라서 익명구현객체와 람다식은 비슷하게 사용되지만 전혀 다른 문법이다.

 

종합적으로 정리해 보자면 익명구현객체는 따로 구현하지않고 바로 객체화시켜서 사용할때 사용하는 문법이고,

람다식은 함수형 인터페이스로서 사용하기 위한 메소드 하나만 참조하여 구현하는 문법이라고 정리해 볼 수 있겠다.

 

 

이 글을 쓴 이유는, 람다식과 익명객체와의 차이를 확인하기위해 정리한 글이다.

(부족한 부분은 추후 개선 예정입니다.)

 

+ 호출은 아래와 같이 호출합니다.

interface MyFunction {
    public abstract int max(int a, int b);
}
class Impl_EX implements MyFunction{ // 함수 호출을 위해 구현을 위한 클래스
    @Override
    public int max(int a, int b) {
        return 0;
    }
}
public class List_Ex {

	/* 람다식 */
    static MyFunction lF = (int a, int b) -> {return a > b ? a : b;};
    
    /*익명 구현 객체로 추상메소드 오버라이딩*/
    static MyFunction mF = new MyFunction() {
        @Override
        public int max(int a, int b) {
            return 0;
        }
    };
    public static void main(String[] args) {
        Impl_EX iE = new Impl_EX();
        int value1 = lF.max(3,5); // 람다식 호출
        int value2 = mF.max(3,5); // 익명 구현 객체를 통한 구현 메소드 호출
        int value3 = iE.max(3,5); // 객체를 통한 구현 메소드 호출
    }
}

번외

이렇게 사용하는지는 잘 모르겠지만 다음과 같이 람다식을 모아놓는 클래스 LambdaExample를 작성하고, 람다식으로 구현한 코드들을 모아놓은(선언) 뒤 해당 클래스를 객체화 하여 타겟타입 변수에 접근하여 함수 호출이 가능하다.

@FunctionalInterface
interface MyFunction {
    public int max(int a, int b);
}

class Example {
    public static void main(String[] args) {
        LamdaExample le = new LamdaExample();
        int max = le.mF.max(1, 2);
        System.out.println("max = " + max);
    }
}
class LamdaExample {
    MyFunction mF = (a, b) ->  a > b ? a : b;
}
int max = le.mF.max(1, 2);

LamdaExample클래스 객체 le로 부터 람다식을 통해 구현한 MyFunction 인터페이스의 변수mf에 접근 후 max() 메소드 호출

 

+

또한 코드에서 보는바와 같이,

    MyFunction1 mF1 = (int a, int b) ->  a > b ? a : b;
    MyFunction2 mF2 = (a, b) ->  a > b ? a : b; // 매개변수 타입 생략 가능

매개변수 타입은 런타임 시에 대입되는 값에 따라 자동으로 인식될 수 있기 때문에 람다식에서는 매개변수의 타입을 일반적으로 언급하지 않는다.

728x90
반응형