3 minute read

📖 람다식 (Lambda)

자바는 크게 2개의 커다란 변화가 있었다.
첫번쨰는 JDK 1.5제네릭의 등장
두번쨰는 JDK 1.8람다식의 등장이다.

프로그래밍은 크게 절차지향, 객체지향, 함수형이 있는데, 람다식의 등장으로 인해서 자바는 객체지향 언어임과 동시에 함수형 언어가 되었다.

절차 지향 프로그래밍(Procedural Programming)

  • 순차적인 처리를 중요시 여기며, 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법

객체지향 프로그래밍(Object Oriented Programming)

  • 모든 데이터를 객체(Object)로 취급하여, 객체가 처리 요청을 받았을 때, 객체 내부에 있는 기능을 가져와 사용해 처리하는 기법
  • 클래스가 일급 객체
    • 유지보수가 뛰어나며 코드 재사용이 용의하다.
    • 처리속도가 느리다.

함수형 프로그래밍(Functional Programming)

  • 순수 함수를 사용해, 상테를 제어하기보단, 빠르게 처리하는데 초점을 둔 방법.
  • 함수가 일급 객체

일급 객체란?

  • 변수에 할당(assignment)할 수 있다.
  • 다른 함수를 인자(argument)로 전달 받는다.
  • 다른 함수의 결과로서 리턴될 수 있다.

람다식 이전에는 interface나 abstract를 사용하여 구현을 하였다.
특히, Arrays.sort()를 사용하기 위해서 Comparator를 사용하거나, 변수 자체에 Compare @Override를 지원해야 하는데, 이를 람다식을 사용해서 단순하게 나타낼 수 있다.


📌 클래스 자체에 직접 선언

public class Book implements Comparable<Book> {
   private int price;

   public Book(int price){
      this.price = price;
  }

  public int getPrice(){
      return this.price;
  }

   @Override
   public int compareTo(Book o) {
       return this.price-o.price;
  }
}

1번째는 class 자체에 비교연산이 가능하게 Comparable 이나 Comparator를 사용하는 방식


📌 Arrays.sort에 Comparator 정렬 규칙 지정

Arrays.sort(books ,new Comparator<Book>() {
   @Override
   public int compare(Book o1, Book o2) {
       return o1.getPrice()-o2.getPrice();
  }
});

Arrays.sort의 인자에 new Comparator라는 정렬 규칙을 지정하는 방식


📌 Arrays.sort에 람다식을 지정

Collections.sort(books, (Book b1, Book b2)-> b1.getPrice()-b2.getPrice());

Arrays.sort의 인자에 람디식을 지정하는 방식


🍄 람다식 사용법

(매개변수) -> { 원하는 처리내용 }
매개변수의 타입이나 반환타입 및 return 생략 또한 가능하다.

(int a, int b) -> { return a > b ? a : b }
(a, b) -> a > b ? a : b


📖 함수형 인터페이스

1개의 추상 메소드를 갖는 인터페이스이며 @FunctionalInterface를 붙여준다.

@FunctionalInterface
interface FunctionalInterfaceClass {
    // abstract method 오직 하나
    int sum();

    // default method 는 존재해도 상관없음
    default void manual() {
        System.out.println("더해주는 함수");
    }

    // static method 는 존재해도 상관없음
    static void staticManual() {
        System.out.println("더해주는 함수의 static");
    }
}

public void main(){
  FunctionalInterfaceClass<String> functionalInterfaceClass = (a, b) -> a + b ;

  int result = functionalInterfaceClass.sum();
  System.out.println(result);
  functionalInterfaceClass.manual();
  functionalInterfaceClass.staticManual();
}


📖 변수 캡처 (Variable Capture)

  • 로컬 변수 캡처
    • final이거나 effective final의 경우만 참조 가능
    • 위를 만족 못하면 concurrency 문제가 발생하여 컴파일이 불가능하다.
  • effective final
    • 사실상 final인 함수
  • 익명 클래스 구현체와 달리 쉐도윙을 하지 않는다.


🍄 변수 캡처 예시

public class Foo {

    public static void main(String[] args){
        Foo foo = new Foo();
        foo.run();
    }

    private void run() {
        // 참조는 할 수 있다.
        // java8 부터는 final 키워드를 생략할 수 있는 케이스가 있다.
        //    이 변수가 사실상 final인 경우이다. (어디서도 이 변수를 변경하지 않는 경우)
        int baseNumber = 10;

        // 1. 로컬 클래스
        class LocalClass{
            void PrintBaseNumber(){
                System.out.println(baseNumber);
            }
        }

        // 2. 익명 클래스
        Consumer<Integer> integerConsumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(baseNumber);
            }
        };

        // 3. 람다
        IntConsumer printInt = (i) -> {
            System.out.println(i + baseNumber);
        };

        printInt.accept(10);
    }
}


🍄 로컬 클래스와 익명 클래스

-> 쉐도잉이 발생

int baseNumber = 10;

// 1. 로컬 클래스
class LocalClass{
    void PrintBaseNumber(){
        int baseNumber = 11;
        // baseNumber 값은 11이 찍힐 것이다. (scope)
        // run 메소드에서 선언한 baseNumber에 대해 쉐도잉이 발생
        System.out.println(baseNumber);
    }
}

// 2. 익명 클래스
Consumer<Integer> integerConsumer = new Consumer<Integer>() {
    @Override
    public void accept(Integer baseNumber) {
        // 파라미터로 전달받은 baseNumber 가 찍힐 것이다.
        // run 메소드에서 선언한 baseNumber에 대해 쉐도잉이 발생
        System.out.println(baseNumber);
    }
};


🍄 람다

-> 쉐도잉이 불가능

int baseNumber = 10;

// 3. 람다
IntConsumer printInt = (baseNumber) -> {
    System.out.println(baseNumber);
};
  • 위와 같이 선언하게 되면 에러가 발생 -> Variable ‘baseNumber’ is already defined in the scope

image


📖 메소드/생성자 레퍼런스

이미 익명클래스를 생성하는 것보다 짧은 람다를 더 줄이는 방법이 바로 메소드/생성자 레퍼런스이다.

(String s) -> Integer.parseInt(s);

Integer::parseInt;

개인 공부 기록용 블로그입니다.
틀리거나 오류가 있을 경우 제보해주시면 감사하겠습니다.😁