lambda란?

함수형 프로그래밍에서 사용되는 개념입니다. 익명(Anonymous) 함수라고 불립니다. 


lambda 특징

1. 추론이 가능한 코드는 제거해 코드가 간결하다 -> 가독성 향상

2. 전달되는 매개변수에 따라서 행위가 결정된다. 

3. 람다식 표현 = (파라미터) -> {몸체}  몸체는 return문이 없는 단 일행 일면, {} 생략 가능

 

코드로 예시를 보겠습니다. 

interface Calculator{  // 추상 메소드가 하나인 인터페이스
    void operation(int a, int b);
}

public class Main {
    public static void main(String[] args) throws IOException {

        Calculator calculator = new Calculator() { // 람다를 사용하지 않은 형태
            @Override
            public void operation(int a, int b) {
                System.out.println(a+b);
            }
        };
        
        Calculator lambdaCalculator = (a,b) -> { // 추상 메소드를 람다식으로 표현
            System.out.println(a+b);
        };
    }
}

lambda와 함수형 인터페이스

이미 제작되어 있는 함수형 인터페이스들이 있습니다. 해당 인터페이스를 하나씩 살펴보겠습니다.

Comparator

정렬을 하는데 이용됩니다. Comparator의 형태입니다.

해당 부분을 lambda를 이용해서 구현해보겠습니다.

public interface Comparator<T> { 
    int compare(T o1, T o2);
}
public class Main {
    public static void main(String[] args) throws IOException {

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(3);
        list.add(5);
        list.add(4);
        list.add(2);


        Collections.sort(list, (a,b) -> { // comareTo(T o1, T o2)에 a와 b가 매개변수로 전달됩니다. 
            return a-b;   // 양수일 경우 바꾸므로 해당 정렬은 오름차순입니다. 
        });
        
        for(Integer now: list){
            System.out.println(now);
        }
        // 출력
        // 1
        // 2      
        // 3       
        // 4       
        // 5

        Collections.sort(list, (a,b)-> {// comareTo(T o1, T o2)에 a와 b가 매개변수로 전달됩니다. 
            return -(a-b); //양수일 경우 바꾸므로 해당 정렬은 내림차순입니다. 
        });

        for(Integer now: list){
            System.out.println(now);
        }
        // 출력
        // 5
        // 4
        // 3
        // 2
        // 1
    }
}

매개 변수가 있고, 반환하는 람다식

interface Calculator{
    int cal(int a, int b);
}

public class Main {
    public static void main(String[] args) throws IOException {

        Calculator c;
        c = (a,b) -> {  // a와 b를 매개변수로 넘깁니다.
            return a+b;
        };
        int cal = c.cal(1, 2);
        System.out.println(cal); // 3 출력
    
        c = (a,b) ->{
            return a-b;
        };

        cal = c.cal(1,2);
        System.out.println(cal); // -1 출력

        c = new Calculator() { // 람다식을 사용하지 않고, Calculator의 cal 함수를 구현한 것입니다. 람다와 같은 결과를 냅니다. 
            @Override
            public int cal(int a, int b) {
                return a-b;
            }
        };

        cal = c.cal(1,2);
        System.out.println(cal); // -1 출력


    }
}

@FunctionalInterface

interface에 하나의 추상 메서드만 존재해야 람다식으로 표현이 가능합니다. @FunctionalInterface를 interface에 붙여 조기 검증이 가능합니다. 

 

  • 추상 메서드가 2개 이상이므로 @FunctioanlInterface에서 컴파일 오류를 확인할 수 있습니다.
  • 혹여나 @FunctionalInterface를 붙이지 않아 검증이 안돼도, 람다식에서 컴파일 오류를 확인할 수 있습니다.

단 허용되는 메서드도 있습니다.

  • defalut 메소드
  • static 메소드


자바에 정의되어 있는 함수형 인터페이스 

자바에서 표준으로 정의하고 있는 함수형 인터페이스들이 있습니다. 하나씩 알아보겠습니다.

 

Predicate

public interface Predicate<T> {
    boolean test(T t);   
}

전달된 인자를 대상으로 true, false 판단합니다.

짝수와 홀수의 합을 출력하는 예시를 보겠습니다.

public class Main {
    public static void main(String[] args) throws IOException {

        List<Integer> list = new ArrayList<>();

        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);

        int evenResult = returnOddSumOrEvenSum(a -> a % 2 == 0, list); // 짝수를 판별합니다.
        System.out.println(evenResult); // 2+4+6 -> 12 출력

        int oddResult = returnOddSumOrEvenSum(a -> a%2==1, list);
        System.out.println(oddResult); // 1+3+5+7 -> 16 출력

    }

    private static int returnOddSumOrEvenSum(Predicate<Integer> p, List<Integer> list){
        int sum=0;

        for(Integer now: list){
            if(p.test(now)){
                sum+=now;
            }
        }
        return sum;
    }
}
  • a -> (a에 대한 판별식) 매개변수로 보낸 것을 판별하는 식을 통해 boolean을 반환합니다. 

Supplier

단순히 무언가를 반환할 때 사용됩니다. 매개변수로 넘기는 것이 없습니다. 

public interface Supplier<T> {
    T get();
}

난수를 생성하고 반환하여 출력하는 예시를 보겠습니다.

public class Main {
    public static void main(String[] args) throws IOException {

        List<Integer> list = new ArrayList<>();

        list = randomList(() -> {
            Random random = new Random(); // T get()을 람다식으로 구현
            return random.nextInt(10); // 10이하의 랜덤수 생성
        }, list);

        for(Integer now: list){
            System.out.println(now); // 만들어진 10개의 수 출력
        }
    }

    private static List<Integer> randomList(Supplier<Integer> s, List<Integer> list){
        for(int i=0; i<10; i++){
            Integer integer = s.get();
            list.add(integer);
        }
        return list;
    }
}
  • T get()을 매개변수 없이 반환하는 람다식을 표현하고, 랜덤 수를 생성하는 것을 볼 수 있습니다.

Consumer

전달된 인자를 기반으로 반환 이외의 다른 실행을 할 수 있습니다.

 

public interface Consumer<T> {
    void accept(T t);
}

난수를 생성하고 출력하는 예시를 보겠습니다.

public class Main {
    public static void main(String[] args) throws IOException {

        List<Integer> list = new ArrayList<>();
        Random random = new Random();

        Consumer<Integer> c = (a) -> { //void accept(T o) 람다식으로 표현
            System.out.println(a);
        };
        for (int i = 0; i < 10; i++) {
            int num = random.nextInt(); // 난수 생성
             c.accept(num);  // 출력
        }
    }
}
  • void accept(T o)를 구현하지만, 반환 없이 출력을 수행합니다.

Function

매개 변수와 반환 값이 모두 존재하며, 2번째 매개변수 형태를 반환합니다.

public interface Function<T, R> {

    R apply(T t);
}

문자열이 들어오고, 문자열의 길이를 반환하는 Function을 만들겠습니다.

public class Main {
    public static void main(String[] args) throws IOException {
        
        List<String> list = new ArrayList<>();

        list.add("robot");
        list.add("cat");
        list.add("university");
        Function<String, Integer> f = a -> { // String인 a가 들어오고 a의 길이를 반환합니다.
            return a.length();
        };

        for(String now: list){
            Integer length = f.apply(now);
            System.out.println(length); // 5, 3, 10 출력됩니다.
        }
    }
}
  • Integer apply(String s)의 형태로 진행됩니다.

정리

람다와 람다 표현식을 이용하여, 코드의 길이를 줄였습니다. 이는 코드의 가독성을 올려줍니다.

지금까지 람다와 람다 표현식을 알아봤습니다. 틀린 부분에 대해서 지적해주시면 감사합니다. 

'JAVA' 카테고리의 다른 글

JAVA Enum  (0) 2022.02.24
JAVA Stream  (0) 2022.02.23
JAVA Optional<T>  (0) 2022.02.23
JAVA Comparable And Comparator  (0) 2022.02.21
Java 제네릭(Generic)  (0) 2022.02.20