개발 무지렁이

[JAVA8] 함수라는 새로운 값의 형식과 값으로 귀결되는 메서드 참조, 그로 인한 간결성 (+ 람다) 본문

Backend/자바

[JAVA8] 함수라는 새로운 값의 형식과 값으로 귀결되는 메서드 참조, 그로 인한 간결성 (+ 람다)

Gaejirang-e 2023. 9. 20. 05:08

𐂂 값으로 귀결될 수 있는 일급 자바 시민
클래스인스턴스화한 결과는 ''으로 귀결되자만
클래스나 클래스 안의 메서드는 그 자체로는 값이 아니다.
이 때 값으로 귀결되지 못하는 클래스나 메서드이급 자바 시민이라 한다.
⚠️ int, double, ..., 객체 모두 그 자체가 값이거나 값으로 귀결되는 일급 자바 시민(일급값)이다.

메서드는 어떻게 해도 값이 아니다.
하지만 메서드를 일급 자바 시민(값)으로 만들면 프로그래밍에 유용하게 활용할 수 있다.

자바8
: 함수새로운 값의 형식으로 추가하고, 함수으로 취급한다.

🦉 new 연산자
: new라는 객체생성 연산자로, 객체 참조생성하여 인스턴스라는 값을 전달할 수 있다.

𐁍 메서드 참조 ::
::라는 연산자로, 메서드 참조를 생성해 함수라는 값을 전달할 수 있다.
[ 이급 자바 시민일급 자바 시민으로 바꿀 수 있는 기능을 추가했다, 메서드 -> 함수(값) ]
여기서 중요한 것은 메서드는 코드를 포함하고 있다는 것이다.
즉, 메서드값으로써 전달할 수 있다는 것은, 코드를 전달할 수 있다는 것을 의미한다.

𖠃 메서드를 값으로 만들면 뭐가 좋을까
🧩 코드가 간결해진다. 🧩 중복코드를 줄일 수 있다.

간결성

📜 자바8이전 코드.java

  File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
      public boolean accept(File file) {
          return file.isHidden();
      }
  });
🦉 코드 해석
: 자바8 이전에는 메서드는 값으로 취급될 수 없었다.
따라서 인수로 메서드를 전달할 수 없고, 위의 코드 같이
(값으로 취급되는) 객체로 감싼 다음, 이 객체인수로써 전달,해야 했다.

📜 자바8이후 코드.java

  File[] hiddenFiles = new File(".").listFiles(File::isHidden);
🦉 코드 해석
: 자바8 이후로는 메서드메서드 참조를 통해 값으로 취급될 수 있다.
:: 연산자를 통해 함수라는 값으로 바꾸면 위의 코드 같이 간결하게 코드를 작성할 수 있다.

중복해소

📜 자바8이전 코드.java

  // 녹색 사과만을 골라내는 필터
  public static List<Apple> filterGreenApples(List<Apple> inventory) {
      List<Apple> result = new ArrayList<>();
      for(Apple apple : inventory) {
          if(GREEN.equals(apple.getColor())) {
              result.add(apple);
          }
      }
      return result;
  }

  // 무거운 사과만을 골라내는 필터
  public static List<Apple> filterHeavyApples(List<Apple> inventory) {
      List<Apple> result = new ArrayList<>();
      for(Apple apple : inventory) {
          if(apple.getWeight() > 150) {
              result.add(apple);
          }
      }
      return result;
  }
🦉 코드 해석
: 위와 같은 필터기능을 하는 2개의 메서드는 사실상
if문을 제외하면 중복된 코드다.

필터
: 조건에 따라 특정항목을 선택해서 반환하는 동작을 '필터'라고 한다.

📜 자바8이후 코드.java

  public static boolean isGreenApple(Apple apple) {
      return GREEN.equals(apple.getColor());
  }

  public static boolean isHeavyApple(Apple apple) {
      return apple.getWeight() > 150;
  }

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

  static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
      List<Apple> result = new ArrayList<>();
      for(Apple apple : inventory) {
          if(p.test(apple)) {
              result.add(apple);
          }
      }
      return result;
  }

  filterApples(inventory, Apple::isGreenApple);
  filterApples(inventory, Apple::isHeavyApple);
🦉 코드 해석
: 위의 코드와 같이 메서드를 값으로써 전달할 수 있다면
메서드 안의 코드도 전달할 수 있으므로,
메서드 참조에 따라 다른 조건으로 필터링하는 하나의 메서드를 만들어, 중복 코드를 제거할 수 있다.

Predicate
: 수학에서는 인수<로 값을 받아 true or false로 반환하는 함수를 '프레디케이트(Predicate)'라고 한다.

𖠃 더 간결하게, 람다 (lambda, 익명함수)
만약, isGreenApple과 isHeavyApple가 한두 번만 사용할 메서드라면,
이를 위해 메서드를 매번 정의하는 것은 비효율적이고, 전체적으로 보면 간결하지도 않다.
이럴 때, 익명함수, 람다를 사용하면 더 간결하게 코드를 작성할 수 있다.

📜 자바8이후 람다 코드.java

  static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
      List<Apple> result = new ArrayList<>();
      for(Apple apple : inventory) {
          if(p.test(apple)) {
              result.add(apple);
          }
      }
      return result;
  }

  filterApples(inventory, (Apple a) -> GREEN.equals(a.getColor()));
  filterApples(inventory, (Apple a) -> a.getWeight() > 150);
🦉 코드 해석
: 람다 문법에서 위의 코드는 ( 인수 ) -> 리턴값;을 의미한다.
재사용 횟수가 현저히 적은 함수라면, 람다(익명함수)로 구현해, 호출하고 없애는게 효율적이다.
Comments