Lambda(μžλ°”μ˜ 정석 3판 μ±… 정리)

μžλ°”μ˜ 정석(3판, μ±…)을 κ³΅λΆ€ν•˜λ©΄μ„œ μ •λ¦¬ν•œ 것

Lambda Expression(λžŒλ‹€μ‹)

  • JDK1.8λΆ€ν„° 좔가됨
  • λžŒλ‹€μ‹μ˜ λ„μž…μœΌλ‘œ 인해 μžλ°”λŠ” 객체지ν–₯언어인 λ™μ‹œμ— ν•¨μˆ˜ν˜• μ–Έμ–΄κ°€ 됨

1. λžŒλ‹€μ‹μ΄λž€?

  • λ©”μ„œλ“œλ₯Ό ν•˜λ‚˜μ˜ 식(expression)으둜 ν‘œν˜„ν•œ 것. λžŒλ‹€μ‹μ€ ν•¨μˆ˜λ₯Ό κ°„λž΅ν•˜λ©΄μ„œλ„ λͺ…ν™•ν•œ μ‹μœΌλ‘œ ν‘œν˜„ν•  수 있게 ν•΄μ€€λ‹€.
  • λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„ν•˜λ©΄ λ©”μ„œλ“œμ˜ 이름과 λ°˜ν™˜κ°’μ΄ μ—†μ–΄μ§€λ―€λ‘œ, λžŒλ‹€μ‹μ„ β€˜μ΅λͺ… ν•¨μˆ˜(anonymous function)β€˜μ΄λΌκ³ λ„ ν•œλ‹€.
int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random() * 5) + 1);
  • μœ„μ—μ„œ (i) -> (int)(Math.random() * 5) + 1이 λ°”λ‘œ λžŒλ‹€μ‹μ΄λ‹€. 이 λžŒλ‹€μ‹μ΄ ν•˜λŠ” 일을 λ©”μ„œλ“œλ‘œ ν‘œν˜„ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.
int method() {
    return (int)(Math.random()*5) + 1;
}
  • μž₯점은?

    • κ°„κ²°ν•˜κ³  μ΄ν•΄ν•˜κΈ° 쉽닀
    • κ°„λ‹¨ν•œ 절차
    • μ›λž˜ λͺ¨λ“  λ©”μ„œλ“œλŠ” ν΄λž˜μŠ€μ— ν¬ν•¨λ˜μ–΄μ•Ό ν•˜λ―€λ‘œ ν΄λž˜μŠ€λ„ μƒˆλ‘œ λ§Œλ“€μ–΄μ•Ό ν•˜κ³ , 객체도 μƒμ„±ν•΄μ•Όλ§Œ λΉ„λ‘œμ†Œ 이 λ©”μ„œλ“œ ν˜ΈμΆœν•  수 μžˆλ‹€.
    • ν•˜μ§€λ§Œ λžŒλ‹€μ‹μ€ μ΄λŸ¬ν•œ λͺ¨λ“  과정없이 였직 λžŒλ‹€μ‹ μžμ²΄λ§ŒμœΌλ‘œλ„ 이 λ©”μ„œλ“œμ˜ 역할을 λŒ€μ‹ ν•  수 μžˆλ‹€.
    • λ©”μ„œλ“œλ₯Ό λ³€μˆ˜μ²˜λŸΌ λ‹€λ£° 수 μžˆλ‹€.
    • λžŒλ‹€μ‹μ€ λ©”μ„œλ“œμ˜ λ§€κ°œλ³€μˆ˜λ‘œ μ „λ‹¬λ˜μ–΄μ§€λŠ” 것이 κ°€λŠ₯ν•˜κ³  λ©”μ„œλ“œμ˜ 결과둜 λ°˜ν™˜λ  수 μžˆλ‹€.

2. λžŒλ‹€μ‹ μž‘μ„±ν•˜κΈ°

  • λžŒλ‹€μ‹μ€ β€˜μ΅λͺ… ν•¨μˆ˜β€™ λ‹΅κ²Œ λ©”μ„œλ“œμ—μ„œ 이름과 λ°˜ν™˜νƒ€μž…μ„ μ œκ±°ν•˜κ³  λ§€κ°œλ³€μˆ˜ 선언뢀와 λͺΈν†΅ {} 사이에 ->λ₯Ό μΆ”κ°€ν•œλ‹€.
λ°˜ν™˜νƒ€μž… λ©”μ„œλ“œμ΄λ¦„ (λ§€κ°œλ³€μˆ˜ μ„ μ–Έ) {
    λ¬Έμž₯λ“€
}
  • μœ„μ—μ„œ λ°˜ν™˜νƒ€μž…κ³Ό λ©”μ„œλ“œμ΄λ¦„μ„ μ—†μ• μ„œ μ•„λž˜μ²˜λŸΌ μ„ μ–Έ
(λ§€κ°œλ³€μˆ˜ μ„ μ–Έ) {
    λ¬Έμž₯λ“€
}
  • ex) 두 κ°’ μ€‘μ—μ„œ 큰 값을 λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œ maxλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λ³€ν™˜ν•˜λ©΄ λ‹€μŒκ³Ό 같이 λ³€ν™”κ°€ 이루어진닀.

    • λžŒλ‹€μ‚¬μš©X

      int max (int a, int b) {
      return a > b ? a : b;
      }
    • λžŒλ‹€μ‚¬μš©O

      (int a, int b) -> {
      return a > b ? a : b;
      }
  • λ°˜ν™˜κ°’μ΄ μžˆλŠ” λ©”μ„œλ“œμ˜ 경우, returnλ¬Έ λŒ€μ‹  β€˜μ‹(expression)으둜 λŒ€μ‹ ν•  수 μžˆλ‹€. μ‹μ˜ μ—°μ‚°κ²°κ³Όκ°€ μžλ™μ μœΌλ‘œ λ°˜ν™˜κ°’μ΄ λœλ‹€.이 λ•ŒλŠ” β€˜λ¬Έμž₯(statement)β€˜μ΄ μ•„λ‹Œ β€˜μ‹β€™μ΄λ―€λ‘œ 끝에 ’;’ 을 뢙이지 μ•ŠλŠ”λ‹€.

    • returnλ¬Έ μƒλž΅μ „
    (int a, int b) -> {return a > b ? a : b;}
    • return λ¬Έ μƒλž΅ ν›„, ; 도 μƒλž΅ν•¨
    (int a, int b) -> a > b ? a : b
  • λžŒλ‹€μ‹μ— μ„ μ–Έλœ λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ€ 좔둠이 κ°€λŠ₯ν•œ κ²½μš°λŠ” μƒλž΅ν•  수 μžˆλŠ”λ°, λŒ€λΆ€λΆ„μ˜ κ²½μš°μ—λŠ” μƒλž΅κ°€λŠ₯ν•˜λ‹€. λžŒλ‹€μ‹μ— λ°˜ν™˜νƒ€μž…μ΄ μ—†λŠ” μ΄μœ λ„ 항상 좔둠이 κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

    • λ°˜ν™˜νƒ€μž… μƒλž΅ μ „
    (int a, int b) -> a > b ? a : b
    • λ°˜ν™˜νƒ€μž… μƒλž΅ ν›„
    (a, b) -> a > b ? : a : b
    • 주의) (int a, b) -> a > b ? a : b와 같이 두 λ§€κ°œλ³€μˆ˜ 쀑 μ–΄λŠ ν•˜λ‚˜μ˜ νƒ€μž…λ§Œ μƒλž΅ν•˜λŠ” 것은 ν—ˆμš©λ˜μ§€ μ•ŠλŠ”λ‹€.
  • μ„ μ–Έλœ λ§€κ°œλ³€μˆ˜κ°€ ν•˜λ‚˜λΏμΈ κ²½μš°μ—λŠ” κ΄„ν˜Έ()λ₯Ό μƒλž΅ν•  수 μžˆλ‹€. 단, λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ΄ 있으면 κ΄„ν˜Έ()λ₯Ό μƒλž΅ν•  수 μ—†λ‹€.

    • κ΄„ν˜Έ() μƒλž΅ μ „

      (a) -> a * a
      (int a) -> a * a
    • κ΄„ν˜Έ() μƒλž΅ ν›„

      a -> a * a // OK
      int a -> a * a // Error
  • κ΄„ν˜Έ{}μ•ˆμ˜ λ¬Έμž₯이 ν•˜λ‚˜μΌ κ²½μš°λ„€λŠ” κ΄„ν˜Έ{}λ₯Ό μƒλž΅ν•  수 μžˆλ‹€. 이 λ•Œ λ¬Έμž₯의 끝에 ;λ₯Ό 뢙이지 μ•Šμ•„μ•Ό ν•œλ‹€λŠ” 것에 주의

    • κ΄„ν˜Έ{} μƒλž΅ μ „
    (String name, int i) -> {
      System.out.println(name+"="+i);
    }
    • κ΄„ν˜Έ{} μƒλž΅ ν›„, 그리고 끝에 ; μƒλž΅ν•¨
    (String name, int i) ->
      System.out.println(name+"="+i)
    • 주의) κ΄„ν˜Έ{}μ•ˆμ˜ λ¬Έμž₯이 return문일 경우 κ΄„ν˜Έ {}λ₯Ό μƒλž΅ν•  수 μ—†λ‹€.
    (int a, int b) -> { return a > b ? a : b;} // OK
    (int a, int b) ->  return a > b ? a : b // Error
  • λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λ³€ν™˜ μ—°μŠ΅1

    • λ©”μ„œλ“œ
    int max (int a, int b) {
      return a > b ? a : b;
    }
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„1
    (int a, int b) -> { return a > b ? a : b;}
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„2
    (int a, int b) -> a > b ? a : b
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„3
    (a, b) -> a > b ? a : b
  • λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λ³€ν™˜ μ—°μŠ΅2

    • λ©”μ„œλ“œ

      void printVar (String name, int i) {
      System.out.println(name + "=" + i);
      }
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„1

      (String name, int i) -> {System.out.println(name + "=" + i);}
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„2

      (name, i) -> {System.out.println(name + "=" + i);}
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„3

      (name, i) -> System.out.println(name + "=" + i)
  • λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λ³€ν™˜ μ—°μŠ΅3

    • λ©”μ„œλ“œ
    int squre (int x) {
      return x * x;
    }
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„1
    (int x) ->  x * x
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„2
    (x) -> x * x
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„3
    x -> x * x
  • λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λ³€ν™˜ μ—°μŠ΅4

    • λ©”μ„œλ“œ
    int roll() {
      return (int) (Math.random() * 6);
    }
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„1
    () -> {return (int) Math.random() * 6;}
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„2
    () -> (int) Math.random() * 6
  • λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λ³€ν™˜ μ—°μŠ΅5

    • λ©”μ„œλ“œ
    int sumArr (int[] arr) {
      int sum = 0;
      for (int i : arr)
        sum += i;
      return sum;
    }
    • λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„1
    (int[] arr) -> {
      int sum = 0;
      for (int i : arr)
        sum += i;
      return sum;
    }

3. ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(Functional Interface)

  • λžŒλ‹€μ‹μ€ 읡λͺ… 클래슀의 객체와 λ™μΌν•˜λ‹€.

    • μ™œλƒν•˜λ©΄ 사싀 μžλ°”μ—μ„œ λͺ¨λ“  λ©”μ„œλ“œλŠ” 클래슀 내에 ν¬ν•¨λ˜μ–΄μ•Ό ν•˜λ―€λ‘œ.
  • κ·Έλž˜μ„œ μ•„λž˜μ˜ λžŒλ‹€μ‹μ€ 사싀 읡λͺ… 클래슀의 객체둜 ν‘œν˜„κ°€λŠ₯ν•˜λ‹€.

    • λžŒλ‹€λ‘œ

      (int a, inb b) -> a > b ? a : b;
    • 읡λͺ… 클래슀의 객체둜

      new Object() {
      int max (int a, int b) {
        return a > b ? a : b;
      }
      }
  • μœ„μ˜ μ½”λ“œμ—μ„œ λ©”μ„œλ“œ 이름 maxλŠ” μž„μ˜λ‘œ 뢙인 것일 뿐 μ˜λ―ΈλŠ” μ—†λ‹€. μ–΄μ¨Œλ“  λžŒλ‹€μ‹μœΌλ‘œ μ •μ˜λœ 읡λͺ… 객체의 λ©”μ„œλ“œλ₯Ό μ–΄λ–»κ²Œ ν˜ΈμΆœν•  것인가? 이미 μ•Œκ³  μžˆλŠ” κ²ƒμ²˜λŸΌ μ°Έμ‘°λ³€μˆ˜κ°€ μžˆμ–΄μ•Ό 객체의 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  수 μžˆμœΌλ‹ˆκΉŒ 일단 이 읡λͺ… 객체의 μ£Όμ†Œλ₯Ό fλΌλŠ” μ°Έμ‘°λ³€μˆ˜μ— μ €μž₯ν•΄λ³΄μž
νƒ€μž… f = (int a, int b) -> a > b ? a : b; // μ°Έμ‘°λ³€μˆ˜μ˜ νƒ€μž…μ„ 뭘둜 ν•΄μ•Ό ν• κΉŒ?
  • 그러면, μ°Έμ‘°λ³€μˆ˜ f의 νƒ€μž…μ€ μ–΄λ–€ 것이어야 ν•˜λ‚˜? μ°Έμ‘°ν˜•μ΄λ―€λ‘œ 클래슀 λ˜λŠ” μΈν„°νŽ˜μ΄μŠ€κ°€ κ°€λŠ₯함. 그리고 λžŒλ‹€μ‹κ³Ό λ™λ“±ν•œ λ©”μ„œλ“œκ°€ μ •μ˜λ˜μ–΄ μžˆλŠ” 것이어야 ν•œλ‹€. κ·Έλž˜μ•Όλ§Œ μ°Έμ‘°λ³€μˆ˜λ‘œ 읡λͺ… 객체(λžŒλ‹€μ‹)의 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  수 있기 λ•Œλ¬Έμ΄λ‹€.
  • 예λ₯Ό λ“€μ–΄, μ•„λž˜μ™€ 같이 max()λΌλŠ” λ©”μ„œλ“œκ°€ μ •μ˜λœ MyFunctionμΈν„°νŽ˜μ΄μŠ€κ°€ μ •μ˜λ˜μ–΄ μžˆλ‹€κ³  κ°€μ •ν•˜μž.
interface MyFunction {
    public abstract int max(int a, int b);
}

그러면 이 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 읡λͺ… 클래슀의 κ°μ²΄λŠ” λ‹€μŒκ³Ό 같이 생성 κ°€λŠ₯

MyFunction f = new Function() {
    public int max(int a, int b) {
        return a > b ? a : b;
    };
}
int big = f.max(3, 5); // 읡λͺ… 객체의 클래슀λ₯Ό 호좜

그런데 μœ„μ˜ MyFunctionμΈν„°νŽ˜μ΄μŠ€μ— μ •μ˜λœ λ©”μ„œλ“œ max()λŠ” λžŒλ‹€μ‹ (int a, int b) -> a > b ? a : bκ³Ό λ©”μ„œλ“œμ˜ μ„ μ–ΈλΆ€κ°€ μΌμΉ˜ν•œλ‹€. κ·Έλž˜μ„œ μœ„ μ½”λ“œμ˜ 읡λͺ… 객체λ₯Ό λžŒλ‹€μ‹μœΌλ‘œ μ•„λž˜μ™€ 같이 λŒ€μ²΄ν•  수 μžˆλ‹€.

MyFunction f = (int a, int b) -> a > b ? a : b; // 읡λͺ… 객체λ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λŒ€μ²΄
ing big = f.max(5, 3); // 읡λͺ… 객체의 λ©”μ„œλ“œλ₯Ό 호좜
  • (μ€‘μš”) 이처럼 MyFunctionμΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 읡λͺ… 객체λ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λŒ€μ²΄κ°€ κ°€λŠ₯ν•œ μ΄μœ λŠ”, λžŒλ‹€μ‹λ„ μ‹€μ œλ‘œλŠ” 읡λͺ… 객체이고, MyFunctionμΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 읡λͺ… 객체의 λ©”μ„œλ“œ max()와 λžŒλ‹€μ‹μ˜ λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…κ³Ό 개수 그리고 λ°˜ν™˜κ°’μ΄ μΌμΉ˜ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. ν•˜λ‚˜μ˜ λ©”μ„œλ“œκ°€ μ„ μ–Έλœ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•΄μ„œ λžŒλ‹€μ‹μ„ λ‹€λ£¨λŠ” 것은 기쑴의 μžλ°”μ˜ κ·œμΉ™λ“€μ„ 어기지 μ•ŠμœΌλ©΄μ„œλ„ μžμ—°μŠ€λŸ¬μ›€.
  • κ·Έλž˜μ„œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 λžŒλ‹€μ‹μ„ λ‹€λ£¨κΈ°λ‘œ κ²°μ •λœ 것이며, λžŒλ‹€μ‹μ„ 닀루기 μœ„ν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(functional interface)라고 λΆ€λ₯΄κΈ°λ‘œ ν•œ 것.
@FunctionalInterface
interface MyFunction { // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ MyFunction을 μ •μ˜
    public abstract int max(int a, int b);
}
  • μ œμ•½) 단, ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ—λŠ” 였직 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ§Œ μ •μ˜λ˜μ–΄ μžˆμ–΄μ•Ό ν•œλ‹€. κ·Έλž˜μ•Ό λžŒλ‹€μ‹κ³Ό μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ„œλ“œκ°€ 1:1둜 연결될 수 있기 λ•Œλ¬Έ. λ°˜λ©΄μ— staticλ©”μ„œλ“œμ™€ defaultλ©”μ„œλ“œμ˜ κ°œμˆ˜μ—λŠ” μ œμ•½μ΄ μ—†λ‹€.

    • μ°Έκ³ ) @FunctionalInterface λ₯Ό 뢙이면, μ»΄νŒŒμΌλŸ¬κ°€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ˜¬λ°”λ₯΄κ²Œ μ •μ˜ν•˜μ˜€λŠ”μ§€ ν™•μΈν•΄μ£Όλ―€λ‘œ, κΌ­ λΆ™μ΄μž
  • κΈ°μ‘΄μ—λŠ” μ•„λž˜μ™€ 같이 μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ„œλ“œ ν•˜λ‚˜λ₯Ό κ΅¬ν˜„ν•˜λŠ”λ°λ„ λ³΅μž‘ν•˜κ²Œ ν–ˆμ–΄μ•Ό ν–ˆλŠ”λ°,
List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaA");

Collections.sort(list, new Comparator<String>(){
    public int compare(String s1, String s2) {
        return s2.compareTo(s1);
    }
});
  • μ΄μ œλŠ” λžŒλ‹€μ‹μœΌλ‘œ μ•„λž˜μ™€ 같이 κ°„λ‹¨νžˆ μ²˜λ¦¬ν•  수 있게 λ˜μ—ˆλ‹€
List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");
Collections.sort(list, (s1, s2) -> s2.compareTo(s1));

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ˜ λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜νƒ€μž…

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ MyFunction이 μ•„λž˜μ™€ 같이 μ •μ˜λ˜μ–΄ μžˆμ„ λ•Œ,
@FunctionalInterface
interface MyFunction {
  void myMethod(); // μΆ”μƒλ©”μ„œλ“œ
}
  • λ©”μ„œλ“œμ˜ λ§€κ°œλ³€μˆ˜κ°€ MyFunctionνƒ€μž…μ΄λ©΄, 이 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œ λžŒλ‹€μ‹μ„ μ°Έμ‘°ν•˜λŠ” μ°Έμ‘°λ³€μˆ˜λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ 지정해야 ν•œλ‹€λŠ” λœ»μ΄λ‹€.
void aMethod(MyFunction f) {
  f.myMethod();
}
  ...
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);
  • λ˜λŠ” μ°Έμ‘°λ³€μˆ˜ 없이 μ•„λž˜μ™€ 같이 직접 λžŒλ‹€μ‹μ„ λ§€κ°œλ³€μˆ˜λ‘œ μ§€μ •ν•˜λŠ” 것도 κ°€λŠ₯
aMethod(() -> System.out.println("myMethod()")); // λžŒλ‹€μ‹μ„ λ§€κ°œλ³€μˆ˜λ‘œ 지정
  • 그리고 λ©”μ„œλ“œμ˜ λ°˜ν™˜νƒ€μž…μ΄ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€νƒ€μž…μ΄λΌλ©΄, 이 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ μΆ”μƒλ©”μ„œλ“œμ™€ λ™λ“±ν•œ λžŒλ‹€μ‹μ„ κ°€λ¦¬ν‚€λŠ” μ°Έμ‘°λ³€μˆ˜λ₯Ό λ°˜ν™˜ν•˜κ±°λ‚˜ λžŒλ‹€μ‹μ„ 직접 λ°˜ν™˜ν•  수 μžˆλ‹€.
MyFunction myMethod() {
  MyFunction f = () -> {};
  return f;     // 이 쀄과 μœ— 쀄을 ν•œ μ€„λ‘œ 쀄이면, return () -> {};
}
  • μ΄λ ‡κ²Œ λžŒλ‹€μ‹μ„ μ°Έμ‘°λ³€μˆ˜λ‘œ λ‹€λ£° 수 μžˆλ‹€λŠ” 것은 λ©”μ„œλ“œλ₯Ό 톡해 λžŒλ‹€μ‹μ„ 주고받을 수 μžˆλ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€. 즉, λ³€μˆ˜μ²˜λŸΌ λ©”μ„œλ“œλ₯Ό μ£Όκ³ λ°›λŠ” 것이 κ°€λŠ₯해진 것이닀. 사싀상 λ©”μ„œλ“œκ°€ μ•„λ‹ˆλΌ 객체λ₯Ό μ£Όκ³ λ°›λŠ” κ²ƒμ΄λ―€λ‘œ 근본적으둜 달라진 것은 아무것도 μ—†μ§€λ§Œ, λžŒλ‹€μ‹ 덕뢄에 μ˜ˆμ „λ³΄λ‹€ μ½”λ“œκ°€ 더 κ°„κ²°ν•˜κ³  μ΄ν•΄ν•˜κΈ° μ‰¬μ›Œμ‘Œλ‹€.
  • 예제 14-1)
@FunctionalInterface
interface MyFunction {
    void run();
}

class LambdaEx1 {
    static void execute(MyFunction f) { // λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ΄ MyFunction인 λ©”μ„œλ“œ
        f.run();
    }

    static MyFunction getMyFunction() { // λ°˜ν™˜ νƒ€μž…μ΄ MyFunction인 λ©”μ„œλ“œ
        MyFunction f = () -> System.out.println("f3.run()");
        return f;
    }

    public static void main(String[] args) {
        // λžŒλ‹€μ‹μœΌλ‘œ MyFunction의 run() 을 κ΅¬ν˜„
        MyFunction f1 = () -> System.out.println("f1.run()");

        MyFunction f2 = new MyFunction() { // 읡λͺ…ν΄λž˜μŠ€λ‘œ run()을 κ΅¬ν˜„
            public void run() { // public을 λ°˜λ“œμ‹œ λΆ™μ—¬μ•Ό 함
                System.out.println("f2.run()");
            }
        };

        MyFunction f3 = getMyFunction();
        f1.run();
        f2.run();
        f3.run();

        execute(f1);
        execute(() -> System.out.println("run()"));
    }

}

/////////////////////////////////
(좜λ ₯)
f1.run()
f2.run()
f3.run()
f1.run()
run()

λžŒλ‹€μ‹μ˜ νƒ€μž…κ³Ό ν˜•λ³€ν™˜

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ λžŒλ‹€μ‹μ„ μ°Έμ‘°ν•  수 μžˆλŠ” 것일 뿐, λžŒλ‹€μ‹μ˜ νƒ€μž…μ΄ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ νƒ€μž…κ³Ό μΌμΉ˜ν•˜λŠ” 것은 μ•„λ‹ˆλ‹€. λžŒλ‹€μ‹μ€ 읡λͺ… 객체이고 읡λͺ… κ°μ²΄λŠ” νƒ€μž…μ΄ μ—†λ‹€. μ •ν™•νžˆλŠ” νƒ€μž…μ€ μžˆμ§€λ§Œ μ»΄νŒŒμΌλŸ¬κ°€ μž„μ˜λ‘œ 이름을 μ •ν•˜κΈ° λ•Œλ¬Έμ— μ•Œ 수 μ—†λŠ” 것이닀. κ·Έλž˜μ„œ λŒ€μž… μ—°μ‚°μžμ˜ μ–‘λ³€μ˜ νƒ€μž…μ„ μΌμΉ˜μ‹œν‚€κΈ° μœ„ν•΄ μ•„λž˜μ™€ 같이 ν˜•λ³€ν™˜μ΄ ν•„μš”ν•˜λ‹€.
  • μ°Έκ³ ) MyFunction은 interface MyFunction {void method();} 와 같이 μ •μ˜λ˜μ—ˆλ‹€κ³  κ°€μ •ν•˜μ˜€λ‹€.
MyFunction f = (MyFunction) (() -> {}); // μ–‘λ³€μ˜ νƒ€μž…μ΄ λ‹€λ₯΄λ―€λ‘œ ν˜•λ³€ν™˜μ΄ ν•„μš”
  • λžŒλ‹€μ‹μ€ MyFunctionμΈν„°νŽ˜μ΄μŠ€λ₯Ό 직접 κ΅¬ν˜„ν•˜μ§€ μ•Šμ•˜μ§€λ§Œ, 이 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 클래슀의 객체와 μ™„μ „νžˆ λ™μΌν•˜κΈ° λ•Œλ¬Έμ— μœ„μ™€ 같은 ν˜•λ³€ν™˜μ„ ν—ˆμš©ν•œλ‹€. 그리고 이 ν˜•λ³€ν™˜μ€ μƒλž΅κ°€λŠ₯ν•˜λ‹€.
  • λžŒλ‹€μ‹μ€ 이름이 없을 뿐 λΆ„λͺ…νžˆ 객체인데도, μ•„λž˜μ™€ 같이 Objectνƒ€μž…μœΌλ‘œ ν˜•λ³€ν™˜ ν•  수 μ—†λ‹€.λžŒλ‹€μ‹μ€ 였직 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œλ§Œ ν˜•λ³€ν™˜μ΄ κ°€λŠ₯ν•˜λ‹€.
Object object = (Object) (() -> {}); // μ—λŸ¬. ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œλ§Œ ν˜•λ³€ν™˜ κ°€λŠ₯
  • ꡳ이 Objectνƒ€μž…μœΌλ‘œ ν˜•λ³€ν™˜ν•˜λ €λ©΄, λ¨Όμ € ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ λ³€ν™˜ν•΄μ•Ό ν•œλ‹€.
Object obj = (Object)(MyFunction) (() -> {});
String str = ((Object)(MyFunction) (() -> {})).toString();
  • 예제 14-2)
@FunctionalInterface
interface MyFunction {
    void myMethod(); // public abstract void myMethod();
}

public class LambdaEx2 {
    public static void main(String[] args) {
        MyFunction f = () -> {}; // MyFunction f = (MyFunction)(() -> {});
        Object obj = (MyFunction) (() -> {}); // Object νƒ€μž…μœΌλ‘œ ν˜•λ³€ν™˜μ΄ μƒλž΅λ¨
        String str = ((Object)(MyFunction)(() -> {})).toString();

        System.out.println(f); // LambdaEx2$$Lambda$1/1324119927@3d075dc0
        System.out.println(obj); // LambdaEx2$$Lambda$2/1078694789@214c265e
        System.out.println(str); // LambdaEx2$$Lambda$3/1831932724@682a0b20

//        System.out.println(() -> {}); // μ—λŸ¬. λžŒλ‹€μ‹μ€ Objectνƒ€μž…μœΌλ‘œ ν˜•λ³€ν™˜ μ•ˆλ¨
        System.out.println((MyFunction)(() -> {})); // LambdaEx2$$Lambda$4/1149319664@7cca494b
//        System.out.println((MyFunction)(() -> {}).toString()); // μ—λŸ¬
        System.out.println(((Object)(MyFunction)(() -> {})).toString()); // LambdaEx2$$Lambda$5/2074407503@3b9a45b3
    }

}
  • 예제 14-2) μ—μ„œ λ³Ό 수 μžˆλ“―μ΄ μ‹€ν–‰κ²°κ³Όλ₯Ό 보면, μ»΄νŒŒμΌλŸ¬κ°€ λžŒλ‹€μ‹μ˜ νƒ€μž…μ„ μ–΄λ–€ ν˜•μ‹μœΌλ‘œ λ§Œλ“€μ–΄ λ‚΄λŠ”μ§€ μ•Œ 수 μžˆλ‹€.

    • 일반적인 읡λͺ… 객체일 경우 객체의 νƒ€μž… : μ™ΈλΆ€ν΄λž˜μŠ€$번호
    • λžŒλ‹€μ‹μ˜ νƒ€μž… : `μ™ΈλΆ€ν΄λž˜μŠ€$LambdaLambda번호

μ™ΈλΆ€ λ³€μˆ˜λ₯Ό μ°Έμ‘°ν•˜λŠ” λžŒλ‹€μ‹

  • λžŒλ‹€μ‹λ„ 읡λͺ… 객체, 즉 읡λͺ… 클래슀의 μΈμŠ€ν„΄μŠ€μ΄λ―€λ‘œ λžŒλ‹€μ‹μ—μ„œ 외뢀에 μ„ μ–Έλœ λ³€μˆ˜μ— μ ‘κ·Όν•˜λŠ” κ·œμΉ™μ€ 읡λͺ… ν΄λž˜μŠ€μ—μ„œ 배운 것과 λ™μΌν•˜λ‹€.
  • 예제 14-3)
import com.sun.org.apache.xerces.internal.dom.PSVIAttrNSImpl;

@FunctionalInterface
interface MyFunction {
    void myMethod();
}

class Outer {
    int val = 10;  // Outer.this.val

    class Inner {
        int val = 20; // this.val

        void method(int i) { // void method(final int i) { 와 동일
            int val = 30; // final int val = 30;
//            i = 10; // μ—λŸ¬. μƒμˆ˜μ˜ 값을 λ³€κ²½ν•  수 μ—†μŒ.

            MyFunction f = () -> {
                System.out.println("                 i : " + i);  // 100
                System.out.println("               val : " + val); // 30
                System.out.println("          this.val : " + ++this.val); // 21
                System.out.println("    Outer.this.val : " + ++Outer.this.val); // 11
            };

            f.myMethod();
        }
    } // Inner클래슀 끝
} // Outer 클래슀 끝

public class LambdaEx3 {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.method(100);
    }
}
  • 예제 14-3) μ—μ„œλŠ”, λžŒλ‹€μ‹ λ‚΄μ—μ„œ μ°Έμ‘°ν•˜λŠ” μ§€μ—­λ³€μˆ˜λŠ” final이 뢙지 μ•Šμ•˜μ–΄λ„ μƒμˆ˜λ‘œ κ°„μ£Όλœλ‹€. λžŒλ‹€μ‹ λ‚΄μ—μ„œ μ§€μ—­λ³€μˆ˜ i와 val을 μ°Έμ‘°ν•˜κ³  μžˆμœΌλ―€λ‘œ λžŒλ‹€μ‹ λ‚΄μ—μ„œλ‚˜ λ‹€λ₯Έ μ–΄λŠ κ³³μ—μ„œλ„ 값을 λ³€κ²½ν•˜λŠ” 일은 ν—ˆμš©λ˜μ§€ μ•ŠλŠ”λ‹€. λ°˜λ©΄μ— Innerν΄λž˜μŠ€μ™€ Outer클래슀의 μΈμŠ€ν„΄μŠ€ λ³€μˆ˜μΈ this.val와 Outer.this.val은 μƒμˆ˜λ‘œ κ°„μ£Όλ˜μ§€ μ•ŠμœΌλ―€λ‘œ 값을 변경해도 λœλ‹€.

    • μ™ΈλΆ€ μ§€μ—­λ³€μˆ˜μ™€ 같은 μ΄λ¦„μ˜ λžŒλ‹€μ‹ λ§€κ°œλ³€μˆ˜λŠ” ν—ˆμš©λ˜μ§€ μ•ŠλŠ”λ‹€. (μ•„λž˜μ—μ„œ μ—λŸ¬2)
    void method(int i) {
    int val = 30; // final int val = 30;
    i = 10; // μ—λŸ¬1. μƒμˆ˜μ˜ 값을 λ³€κ²½ν•  수 μ—†μŒ.
    
    MyFunction f = (i) -> { // μ—λŸ¬2. μ™ΈλΆ€ μ§€μ—­λ³€μˆ˜μ™€ 이름이 쀑볡됨.
        System.out.println("                 i : " + i);
        System.out.println("               val : " + val);
        System.out.println("          this.val : " + ++this.val);
        System.out.println("    Outer.this.val : " + ++Outer.this.val);
    }
    }

4. java.util.function νŒ¨ν‚€μ§€

  • λŒ€λΆ€λΆ„μ˜ λ©”μ„œλ“œλŠ” νƒ€μž…μ΄ λΉ„μŠ·ν•˜λ‹€. λ§€κ°œλ³€μˆ˜κ°€ μ—†κ±°λ‚˜ ν•œ 개 λ˜λŠ” 두 개, λ°˜ν™˜ 값은 μ—†κ±°λ‚˜ ν•œ 개. κ²Œλ‹€κ°€ 지넀릭 λ©”μ„œλ“œλ‘œ μ •μ˜ν•˜λ©΄ λ§€κ°œλ³€μˆ˜λ‚˜ λ°˜ν™˜ νƒ€μž…μ΄ 달라도 λ¬Έμ œκ°€ λ˜μ§€ μ•ŠλŠ”λ‹€. κ·Έλž˜μ„œ java.util.function νŒ¨ν‚€μ§€μ— 일반적으둜 자주 μ“°μ΄λŠ” ν˜•μ‹μ˜ λ©”μ„œλ“œλ₯Ό ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ 미리 μ •μ˜ν•΄ 놓은 것. 맀번 μƒˆλ‘œμš΄ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•˜μ§€ 말고, κ°€λŠ₯ν•˜λ©΄ 이 νŒ¨ν‚€μ§€μ˜ μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•˜λŠ” 것이 μ’‹λ‹€.
  • κ·Έλž˜μ•Ό ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ— μ •μ˜λœ λ©”μ„œλ“œ 이름도 ν†΅μΌλ˜κ³ , μž¬μ‚¬μš©μ„±μ΄λ‚˜ μœ μ§€λ³΄μˆ˜ μΈ‘λ©΄μ—μ„œλ„ μ’‹λ‹€. 자주 μ“°μ΄λŠ” κ°€μž₯ 기본적인 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” λ‹€μŒκ³Ό κ°™λ‹€. (참고둜 νƒ€μž… 문자 TλŠ” Type을, R은 Return Type을 μ˜λ―Έν•œλ‹€)
ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ λ©”μ„œλ“œ μ„€λͺ…
java.lang.Runnable void run() λ§€κ°œλ³€μˆ˜λ„ μ—†κ³ , λ°˜ν™˜κ°’λ„ μ—†μŒ
Supplier<T> void accept(T t) β€”Tβ€”> λ§€κ°œλ³€μˆ˜λ„ μ—†κ³ , λ°˜ν™˜κ°’λ§Œ 있음
Consumer<T> β€”Tβ€”> void accept(T t) Supplier와 λ°˜λŒ€λ‘œ λ§€κ°œλ³€μˆ˜λ§Œ 있고, λ°˜ν™˜κ°’μ΄ μ—†μŒ
Function<T,R> β€”Tβ€”> R apply(T t) β€”Rβ€”> 일반적인 ν•¨μˆ˜, ν•˜λ‚˜μ˜ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›μ•„μ„œ κ²°κ³Όλ₯Ό λ°˜ν™˜
Predicate<T> β€”Tβ€”> boolean test(T t) β€”booleanβ€”> 쑰건식을 ν‘œν˜„ν•˜λŠ”λ° μ‚¬μš©λ¨. λ§€κ°œλ³€μˆ˜λŠ” ν•˜λ‚˜, λ°˜ν™˜ νƒ€μž…μ€ boolean
  • λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜κ°’μ˜ μœ λ¬΄μ— 따라 4개의 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ μ •μ˜λ˜μ–΄ 있고, Function의 λ³€ν˜•μœΌλ‘œ Predicateκ°€ μžˆλŠ”λ°, λ°˜ν™˜κ°’μ΄ booleanμ΄λΌλŠ” κ²ƒλ§Œ μ œμ™Έν•˜λ©΄ Functionκ³Ό λ™μΌν•˜λ‹€. PredicateλŠ” 쑰건식을 ν•¨μˆ˜λ‘œ ν‘œν˜„ν•˜λŠ”λ° μ‚¬μš©λœλ‹€.

μ‘°κ±΄μ‹μ˜ ν‘œν˜„μ— μ‚¬μš©λ˜λŠ” Predicate

  • PredicateλŠ” Function의 λ³€ν˜•μœΌλ‘œ, λ°˜ν™˜νƒ€μž…μ΄ booleanμ΄λΌλŠ” κ²ƒλ§Œ λ‹€λ₯΄λ‹€. PredicateλŠ” 쑰건식을 λžŒλ‹€μ‹μ„ ν‘œν˜„ν•˜λŠ”λ° μ‚¬μš©λœλ‹€.

    • μ°Έκ³ ) μˆ˜ν•™μ—μ„œ 결과둜 true λ˜λŠ” falseλ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ₯Ό β€˜ν”„λ ˆλ””μΌ€μ΄νŠΈ(predicate)β€˜λΌκ³  ν•œλ‹€.
Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";

if (isEmptyStr.test(s)) // if(s.length()) == 0
  System.out.println("This is an empty String.");

λ§€κ°œλ³€μˆ˜κ°€ 두 개인 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • λ§€κ°œλ³€μˆ˜μ˜ κ°œμˆ˜κ°€ 2개인 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” 이름 μ•žμ— 접두사 β€˜Bi’가 λΆ™λŠ”λ‹€.

    • μ°Έκ³ ) λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μœΌλ‘œ 보톡 Tλ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ, μ•ŒνŒŒλ²³μ—μ„œ T λ‹€μŒ 문자인 U, V, Wλ₯Ό λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•˜λŠ” 것일 뿐 별닀λ₯Έ μ˜λ―ΈλŠ” μ—†λ‹€.
ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ λ©”μ„œλ“œ μ„€λͺ…
BiConsumer<T,U> β€”T, Uβ€”> void accept(T t, U u) 두 개의 λ§€κ°œλ³€μˆ˜λ§Œ 있고, λ°˜ν™˜κ°’μ΄ μ—†μŒ
BiPredicate<T,U> β€”T, Uβ€”> boolean test(T t, U u) β€”booleanβ€”> 쑰건식을 ν‘œν˜„ν•˜λŠ”λ° μ‚¬μš©λ¨. λ§€κ°œλ³€μˆ˜λŠ” λ‘˜, λ°˜ν™˜κ°’μ€ boolean
BiFunction<T,U,R> β€”T, Uβ€”> β€˜R apply(T t, U u)’ β€”Rβ€”> 두 개의 λ§€κ°œλ³€μˆ˜λ₯Ό λ°›μ•„μ„œ ν•˜λ‚˜μ˜ κ²°κ³Όλ₯Ό λ°˜ν™˜
  • μ°Έκ³ ) SupplierλŠ” λ§€κ°œλ³€μˆ˜λŠ” μ—†κ³  λ°˜ν™˜κ°’λ§Œ μ‘΄μž¬ν•˜λŠ”λ°, λ©”μ„œλ“œλŠ” 두 개의 값을 λ°˜ν™˜ν•  수 μ—†μœΌλ―€λ‘œ BiSupplierκ°€ μ—†λŠ” 것이닀.
  • 두 개 μ΄μƒμ˜ λ§€κ°œλ³€μˆ˜λ₯Ό κ°–λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ ν•„μš”ν•˜λ‹€λ©΄ 직접 λ§Œλ“€μ–΄μ„œ μ¨μ•Όν•œλ‹€. 만일 3개의 λ§€κ°œλ³€μˆ˜λ₯Ό κ°–λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ„ μ–Έν•œλ‹€λ©΄ λ‹€μŒκ³Ό 같을 것이닀.
@FunctionalInterface
interface TriFunction<T, U, V, R> {
  R apply(T t, U u, V v);
}

UnaryOperator와 BinaryOperator

  • Function의 또 λ‹€λ₯Έ λ³€ν˜•μœΌλ‘œ UnaryOperator와 BinaryOperatorκ°€ μžˆλŠ”λ°, λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…κ³Ό λ°˜ν™˜νƒ€μž…μ˜ νƒ€μž…μ΄ λͺ¨λ‘ μΌμΉ˜ν•œλ‹€λŠ” 점만 μ œμ™Έν•˜κ³ λŠ” Functionκ³Ό κ°™λ‹€.
  • μ°Έκ³ ) UnaryOperator와 BinaryOperator의 쑰상은 각각 Function와 BiFunction이닀.
ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ λ©”μ„œλ“œ μ„€λͺ…
UnaryOperator<T> β€”Tβ€”> `T apply(T t) β€”Tβ€”> | Function의 μžμ†, Functionκ³Ό 달리 λ§€κ°œλ³€μˆ˜μ™€ 결과의 νƒ€μž…μ΄ κ°™λ‹€.
BinaryOperator<T> β€”T, Tβ€”> T apply(T t, T t) β€”tβ€”> BiFunction의 μžμ†, BiFunctionκ³Ό 달리 λ§€κ°œλ³€μˆ˜μ™€ 결과의 νƒ€μž…μ΄ κ°™λ‹€.

μ»¬λ ‰μ…˜ ν”„λ ˆμž„μ›κ³Ό ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • μ»¬λ ‰μ…˜ ν”„λ ˆμž„μ›μ˜ μΈν„°νŽ˜μ΄μŠ€μ— λ‹€μˆ˜μ˜ λ””ν΄νŠΈ λ©”μ„œλ“œκ°€ μΆ”κ°€λ˜μ—ˆλŠ”λ°, κ·Έ μ€‘μ˜ μΌλΆ€λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•œλ‹€. λ‹€μŒμ€ κ·Έ λ©”μ„œλ“œλ“€μ˜ λͺ©λ‘μ΄λ‹€.(μ»¬λ ‰μ…˜ ν”„λ ˆμž„μ›μ˜ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” λ©”μ„œλ“œλ“€)

    • μ°Έκ³ ) λ‹¨μˆœν™”ν•˜κΈ° μœ„ν•΄ μ™€μΌλ“œ μΉ΄λ“œλŠ” μƒλž΅ν•˜μ˜€λ‹€.
μΈν„°νŽ˜μ΄μŠ€ λ©”μ„œλ“œ μ„€λͺ…
Collection boolean removeIf(Predicate<E> filter) 쑰건에 λ§žλŠ” μš”μ†Œλ₯Ό μ‚­μ œ
List void replaceAll(UnaryOperator<E> operator) λͺ¨λ“  μš”μ†Œλ₯Ό λ³€ν™˜ν•˜μ—¬ λŒ€μ²΄
Iterable void forEach(Consumer<T> action) λͺ¨λ“  μš”μ†Œμ— μž‘μ—… action을 μˆ˜ν–‰
Map V compute(K key, BiFunction<K, V, V> f) μ§€μ •λœ ν‚€μ˜ 값에 μž‘μ—… fλ₯Ό μˆ˜ν–‰
Map V computeIfAbsent(K key, Function<K, V> f) ν‚€κ°€ μ—†μœΌλ©΄, μž‘μ—… f μˆ˜ν–‰ ν›„ μΆ”κ°€
Map V computeIfPresent(K key, BiFunction<K, V, V> f) μ§€μ •λœ ν‚€κ°€ μžˆμ„ λ•Œ, μž‘μ—… f μˆ˜ν–‰
Map V merge(K key, V value, BiFunction<V, V, V> f) λͺ¨λ“  μš”μ†Œμ— λ³‘ν•©μž‘μ—… fλ₯Ό μˆ˜ν–‰
Map void forEach(BiConsumer<K, V> action) λͺ¨λ“  μš”μ†Œμ— μž‘μ—… action을 μˆ˜ν–‰
Map void replaceAll(BiFunction<K, V, V> f) λͺ¨λ“  μš”μ†Œμ— μΉ˜ν™˜μž‘μ—… fλ₯Ό μˆ˜ν–‰
  • μ΄λ¦„λ§Œ 봐도 μ–΄λ–€ 일을 ν•˜λŠ” λ©”μ„œλ“œμΈμ§€ μΆ©λΆ„νžˆ μ•Œ 수 μžˆμ„ 것이닀. MapμΈν„°νŽ˜μ΄μŠ€μ— μžˆλŠ” β€˜Computeβ€™λ‘œ μ‹œμž‘ν•˜λŠ” λ©”μ„œλ“œλ“€μ€ 맡의 valueλ₯Ό λ³€ν™˜ν•˜λŠ” 일을 ν•˜κ³  merge()λŠ” Map을 λ³‘ν•©ν•˜λŠ” 일을 ν•œλ‹€. 이 λ©”μ„œλ“œλ“€μ„ μ–΄λ–€ μ‹μœΌλ‘œ μ‚¬μš©ν•˜λŠ”μ§€λŠ” λ‹€μŒμ˜ 예제λ₯Ό 보자.
  • 예제 14-4)
public class LambdaEx4 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0 ; i < 10 ; i++) {
            list.add(i);
        }

        // list λͺ¨λ“  μš”μ†Œ 좜λ ₯
        list.forEach(i -> System.out.print(i + ",")); // 0,1,2,3,4,5,6,7,8,9,
        System.out.println();

        // listμ—μ„œ 2 λ˜λŠ” 3의 배수λ₯Ό μ œκ±°ν•œλ‹€.
        list.removeIf(x -> x % 2==0 || x % 3 ==0); // [1, 5, 7]
        System.out.println(list);

        list.replaceAll(i -> i * 10); // list의 각 μš”μ†Œμ— 10을 κ³±ν•œλ‹€.
        System.out.println(list); // [10, 50, 70]

        Map<String, Object> map = new HashMap<>();
        map.put("1", "1");
        map.put("2", "2");
        map.put("3", "3");
        map.put("4", "4");

        // map의 λͺ¨λ“  μš”μ†Œλ₯Ό {k, v} 의 ν˜•μ‹μœΌλ‘œ 좜λ ₯ν•œλ‹€.
        map.forEach((k, v) -> System.out.print("{" + k + " ," + v + "},")); // {1 ,1},{2 ,2},{3 ,3},{4 ,4},
        System.out.println();
    }
}
  • λ©”μ„œλ“œμ˜ 기본적인 μ‚¬μš©λ²•λ§Œ λ³΄μ—¬μ£ΌλŠ” κ°„λ‹¨ν•œ 예제. 예제의 λžŒλ‹€μ‹μ„ λ³€ν˜•ν•΄μ„œ λ‹€μ–‘ν•˜κ²Œ ν…ŒμŠ€νŠΈν•΄λ³΄κΈ°
  • λ‹€μŒμ€ μ•žμ„œ μ„€λͺ…ν•œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ“€μ„ μ‚¬μš©ν•˜λŠ” 예제.
  • 예제 14-5)
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class LambdaEx5 {

    public static void main(String[] args) {
        Supplier<Integer> s = () -> (int)(Math.random() * 100) + 1;
        Consumer<Integer> c = i -> System.out.print(i + ", ");
        Predicate<Integer> p = i -> i%2 == 0;
        Function<Integer, Integer> f = i -> i/10*10; // i의 1의 자리λ₯Ό μ—†μ•€λ‹€.

        List<Integer> list = new ArrayList<>();
        makeRandomList(s, list);
        System.out.println(list); //  랜덀숫자 λ½‘μŒ [79, 78, 84, 9, 39, 4, 15, 21, 99, 13]
        printEvenNum(p, c, list); //  뽑은 것 μ€‘μ—μ„œ 짝수만 좜λ ₯ [78, 84, 4, ]
        List<Integer> newList = doSomething(f, list);
        System.out.println(newList); // λžœλ€μˆ«μžλ“€ 쀑 1의 자리 μ—†μ•°[70, 70, 80, 0, 30, 0, 10, 20, 90, 10]
    }

    static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
        List<T> newList = new ArrayList<T>(list.size());

        for (T i : list) {
            newList.add(f.apply(i));
        }
        return newList;
    }

    static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
        System.out.print("[");
        for (T i : list) {
            if (p.test(i))
                c.accept(i);
        }
        System.out.println("]");
    }

    static <T> void makeRandomList(Supplier<T> s, List<T> list) {
        for (int i = 0 ; i < 10 ; i++) {
            list.add(s.get());
        }
    }

}

κΈ°λ³Έν˜•μ„ μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • μ§€κΈˆκΉŒμ§€ μ†Œκ°œν•œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜κ°’μ˜ νƒ€μž…μ΄ λͺ¨λ‘ 지넀릭 νƒ€μž…μ΄μ—ˆλŠ”λ°, κΈ°λ³Έν˜• νƒ€μž…μ˜ 값을 μ²˜λ¦¬ν•  λ•Œλ„ 래퍼(wrapper)클래슀λ₯Ό μ‚¬μš©ν•΄μ™”λ‹€. κ·ΈλŸ¬λ‚˜ κΈ°λ³Έν˜• λŒ€μ‹  래퍼클래슀λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 λ‹Ήμ—°νžˆ λΉ„νš¨μœ¨μ μ΄λ‹€. κ·Έλž˜μ„œ 보닀 효율적으둜 μ²˜λ¦¬ν•  수 μžˆλ„λ‘ κΈ°λ³Έν˜•μ„ μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ“€μ΄ μ œκ³΅λœλ‹€.
μΈν„°νŽ˜μ΄μŠ€ λ©”μ„œλ“œ μ„€λͺ…
Double TolIntFunction β€”doubleβ€”> int applyAsInt(double d)β€”intβ€”> AToBFunction은 μž…λ ₯이 Aνƒ€μž… 좜λ ₯이 Bνƒ€μž…
ToIntFunction<T> β€”Tβ€”> int applyAsInt(T value) β€”intβ€”> ToBFunction은 좜λ ₯이 Bνƒ€μž…μ΄λ‹€. μž…λ ₯은 지넀릭 νƒ€μž…
IntFunction<T> β€”intβ€”> R apply(T t, U u) β€”Rβ€”> AFunction은 μž…λ ₯이 Aνƒ€μž…μ΄κ³  좜λ ₯은 지넀릭 νƒ€μž…
ObjIntConsumer<T> β€”T, intβ€”> void accept(T t, U u) ObjAFunction은 μž…λ ₯이 T, Aνƒ€μž…μ΄κ³  좜λ ₯은 μ—†λ‹€.
  • 예제 14-6) 예제 14-5λ₯Ό κΈ°λ³Έν˜•μ„ μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ λ³€κ²½ν•œ 것
import java.util.Arrays;
import java.util.function.IntConsumer;
import java.util.function.IntPredicate;
import java.util.function.IntSupplier;
import java.util.function.IntUnaryOperator;

public class LambdaEx6 {
    public static void main(String[] args) {
        IntSupplier s = () -> (int)(Math.random()*100) + 1;
        IntConsumer c = i -> System.out.print(i + ", ");
        IntPredicate p = i -> i % 2 == 0;
        IntUnaryOperator op = i -> i/10*10; // i의 일의 자리λ₯Ό μ—†μ•€λ‹€.

        int[] arr = new int[10];

        makeRandomList(s, arr);
        System.out.println(Arrays.toString(arr)); // [27, 29, 54, 82, 71, 91, 62, 63, 51, 8]
        printEvenNum(p, c, arr); // [54, 82, 62, 8, ]
        int[] newArr = doSomething(op, arr);
        System.out.println(Arrays.toString(newArr)); // [20, 20, 50, 80, 70, 90, 60, 60, 50, 0]
    }


    static void makeRandomList (IntSupplier s, int[] arr) {
        for (int i = 0 ; i < arr.length ; i++) {
            arr[i] = s.getAsInt(); // get() 이 μ•„λ‹ˆλΌ getAsInt() μž„μ— 주의
        }
    }

    static void printEvenNum (IntPredicate p, IntConsumer c, int[] arr) {
        System.out.print("[");
        for (int i : arr) {
            if (p.test(i))
                c.accept(i);
        }
        System.out.println("]");
    }

    static int[] doSomething(IntUnaryOperator op, int[] arr) {
        int[] newArr = new int[arr.length];

        for (int i = 0 ; i < newArr.length ; i++) {
            newArr[i] = op.applyAsInt(arr[i]); // apply()κ°€ μ•„λ‹˜μ— 주의
        }

        return newArr;
    }
}
  • μœ„ μ˜ˆμ œμ—μ„œ 만일 μ•„λž˜μ™€ 같이 IntUnaryOperator λŒ€μ‹  Function 을 μ‚¬μš©ν•˜λ©΄ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.
Function f = (a) -> 2*a; // μ—λŸ¬. a의 νƒ€μž…μ„ μ•Œ 수 μ—†μœΌλ―€λ‘œ μ—°μ‚°λΆˆκ°€
  • λ§€κ°œλ³€μˆ˜ a와 λ°˜ν™˜ κ°’μ˜ νƒ€μž…μ„ μΆ”μ •ν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€. κ·Έλž˜μ„œ μ•„λž˜μ™€ 같이 νƒ€μž…μ„ 지정해 μ£Όμ–΄μ•Ό ν•œλ‹€.
// OK, λ§€κ°œλ³€μˆ˜ 타이봐 λ°˜ν™˜νƒ€μž…μ΄ Integer
Function<Integer, Integer> f = (a) -> 2*a;
  • λ˜λŠ” μ•„λž˜μ™€ 같이 Function λŒ€μ‹  IntFunction을 μ‚¬μš©ν•  μˆ˜λ„ μžˆμ§€λ§Œ, IntUnaryOperator κ°€ Functionμ΄λ‚˜ IntFunction보닀 μ˜€ν† λ°•μ‹±&μ–Έλ°•μ‹±μ˜ νšŸμˆ˜κ°€ 쀄어듀어 더 μ„±λŠ₯이 μ’‹λ‹€.
// OK, λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ int, λ°˜ν™˜νƒ€μž…μ€ Integer
IntFunction<Integer> f = (a) -> 2 * a;
  • IntFunction, ToIntFunction, IntToLongFunction은 μžˆμ–΄λ„ IntToIntFunction은 μ—†λŠ”λ°, κ·Έ μ΄μœ λŠ” IntUnaryOperatorκ°€ κ·Έ 역할을 ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…κ³Ό λ°˜ν™˜νƒ€μž…μ΄ μΌμΉ˜ν•  λ•ŒλŠ” μ•žμ„œ 배운 κ²ƒμ²˜λŸΌ Function λŒ€μ‹  UnaryOperatorλ₯Ό μ‚¬μš©ν•˜μž.

5. Function의 ν•©μ„±κ³Ό Predicate의 κ²°ν•©

  • μ•žμ„œ μ†Œκ°œν•œ java.util.function νŒ¨ν‚€μ§€μ˜ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” μΆ”μƒλ©”μ„œλ“œ 외에도 λ””ν΄νŠΈ λ©”μ„œλ“œμ™€ staticλ©”μ„œλ“œκ°€ μ •μ˜λ˜μ–΄ μžˆλ‹€. μš°λ¦¬λŠ” Functionκ³Ό Predicate에 μ •μ˜λœ λ©”μ„œλ“œμ— λŒ€ν•΄μ„œλ§Œ μ‚΄νŽ΄λ³Ό 것인데, κ·Έ μ΄μœ λŠ” λ‹€λ₯Έ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ„œλ“œλ„ μœ μ‚¬ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. 이 두 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ— λŒ€ν•œ μ„€λͺ…λ§ŒμœΌλ‘œλ„ μΆ©λΆ„ν•˜λ‹ˆ μ‘μš©μ΄ κ°€λŠ₯ν•  것이닀.
  • μ°Έκ³ ) μ›λž˜ FunctionμΈν„°νŽ˜μ΄μŠ€λŠ” λ°˜λ“œμ‹œ 두 개의 νƒ€μž…μ„ 지정해 μ€˜μ•Όν•˜κΈ° λ•Œλ¬Έμ—, 두 νƒ€μž…μ΄ 같아도 Function<T> 라고 μ“Έ 수 μ—†λ‹€. Function<T, T> 라고 써야 ν•œλ‹€.
  • Function

    • default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)
    • default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
    • static <T> Function<T, T> identify()
  • Predicate

    • default Predicat<T> and(Predicate<? super T> other)
    • default Predicat<T> or(Predicate<? super T> other)
    • default Predicat<T> negate()
    • static <T> Predicate<T> isEqual(Object targetRef)

Function의 ν•©μ„±

  • μˆ˜ν•™μ—μ„œ 두 ν•¨μˆ˜λ₯Ό ν•©μ„±ν•΄μ„œ ν•˜λ‚˜μ˜ μƒˆλ‘œμš΄ ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄λ‚Ό 수 μžˆλŠ” κ²ƒμ²˜λŸΌ, 두 λžŒλ‹€μ‹μ„ ν•©μ„±ν•΄μ„œ μƒˆλ‘œμš΄ λžŒλ‹€μ‹μ„ λ§Œλ“€ 수 μžˆλ‹€. 이미 μ•Œκ³  μžˆλŠ” κ²ƒμ²˜λŸΌ, 두 ν•¨μˆ˜μ˜ 합성은 μ–΄λŠ ν•¨μˆ˜λ₯Ό λ¨Όμ € μ μš©ν•˜λŠλƒμ— 따라 달라진닀. ν•¨μˆ˜ f, gκ°€ μžˆμ„ λ•Œ, f.andThen(g)λŠ” ν•¨μˆ˜ fλ₯Ό λ¨Όμ € μ μš©ν•˜κ³ , κ·Έ λ‹€μŒμ— ν•¨μˆ˜ gλ₯Ό μ μš©ν•œλ‹€. 그리고 f.compose(g)λŠ” λ°˜λŒ€λ‘œ gλ₯Ό λ¨Όμ € μ μš©ν•˜κ³  fλ₯Ό μ μš©ν•œλ‹€.
  • (μžλ°”μ˜ 정석 p809 κ·Έλ¦Ό14-1에 μƒμ„Ένžˆ λ‚˜μ™€μžˆμŒ)

    • andThen()κ³Ό compose() 의 비ꡐ
    • default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)
    • dfault <V> Function<V, R> compose(Function<? super V, ? extends T> before)
  • 예λ₯Ό λ“€μ–΄, λ¬Έμžμ—΄μ„ 숫자둜 λ³€ν™˜ν•˜λŠ” ν•¨μˆ˜ f와 숫자λ₯Ό 2진 λ¬΄μžμ—΄λ‘œ λ³€ν™˜ν•˜λŠ” ν•¨μˆ˜ gλ₯Ό andThen()으둜 ν•©μ„±ν•˜μ—¬ μƒˆλ‘œμš΄ ν•¨μˆ˜ hλ₯Ό λ§Œλ“€μ–΄λ‚Ό 수 μžˆλ‹€.

    • Function<String, Integer> f = (s) -> Integer.parseInt(s, 16);
    • Function<Integer, String> g = (i) -> Integer.toBinaryString(i);
    • Function<String, String> h = f.andThen(g);
  • ν•¨μˆ˜ h의 지넀릭 νƒ€μž…μ΄ <String, String> 이닀. 즉, String을 μž…λ ₯λ°›μ•„μ„œ String을 결과둜 λ°˜ν™˜ν•œλ‹€. 예λ₯Ό λ“€μ–΄ ν•¨μˆ˜ h에 λ¬Έμžμ—΄ β€œFF”λ₯Ό μž…λ ₯ν•˜λ©΄, 결과둜 β€œ11111111”을 μ–»λŠ”λ‹€.

    • System.out.println(h.apply(β€œEE”)); // β€œFF” -> 255 -> β€œ11111111”
  • μ΄λ²ˆμ—” compose()λ₯Ό μ΄μš©ν•΄μ„œ 두 ν•¨μˆ˜λ₯Ό λ°˜λŒ€μ˜ μˆœμ„œλ‘œ ν•©μ„±ν•΄λ³΄μž.

    • Function<Integer, String> g = (i) -> Integer.toBinaryString(i);
    • Function<String, Integer> f = (s) -> Integer.parseInt(s, 16);
    • Function<Integer, Integer> h = f.compose(g);
  • 이전과 달리 ν•¨μˆ˜ h의 지넀릭 νƒ€μž…μ΄ <Integer, Integer> 이닀. ν•¨μˆ˜ h에 숫자 2λ₯Ό μž…λ ₯ν•˜λ©΄, 결과둜 16을 μ–»λŠ”λ‹€.

    • μ°Έκ³ ) ν•¨μˆ˜ fλŠ” β€œ10”을 16μ§„μˆ˜λ‘œ μΈμ‹ν•˜κΈ° λ•Œλ¬Έμ— 16을 결과둜 μ–»λŠ”λ‹€.
    • Systme.out.println(h.apply(2)); // 2 -> β€œ10” -> 16
  • 그리고 identify()λŠ” ν•¨μˆ˜λ₯Ό μ μš©ν•˜κΈ° 이전과 이후가 λ™μΌν•œ β€˜ν•­λ“± ν•¨μˆ˜β€™κ°€ ν•„μš”ν•  λ•Œ μ‚¬μš©ν•œλ‹€. 이 ν•¨μˆ˜λ₯Ό λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„ν•˜λ©΄ β€˜x -> x’ 이닀. μ•„λž˜μ˜ 두 λ¬Έμž₯은 λ™λ“±ν•˜λ‹€.

    • μ°Έκ³ ) ν•­λ“± ν•¨μˆ˜λŠ” ν•¨μˆ˜μ— xλ₯Ό λŒ€μž…ν•˜λ©΄ κ²°κ³Όκ°€ x인 ν•¨μˆ˜λ₯Ό λ§ν•œλ‹€. f(x) = x

    • Function<String, String> f = x -> x; // Function<String, String> f = Function.identify(); // μœ„μ˜ λ¬Έμž₯κ³Ό 동일 System.out.println(f.apply(β€œAAA”)); // AAAκ°€ κ·ΈλŒ€λ‘œ 좜λ ₯됨

    • ν•­λ“± ν•¨μˆ˜λŠ” 잘 μ‚¬μš©λ˜μ§€ μ•ŠλŠ” 편이며, λ‚˜μ€‘μ— 배울 map()으둜 λ³€ν™˜μž‘μ—…ν•  λ•Œ, λ³€ν™˜μ—†μ΄ κ·ΈλŒ€λ‘œ μ²˜λ¦¬ν•˜κ³ μž ν•  λ•Œ μ‚¬μš©λœλ‹€.

Predicate의 κ²°ν•©

  • μ—¬λŸ¬ 쑰건식을 논리 μ—°μ‚°μžμΈ &&(and), ||(or), !(not)으둜 μ—°κ²°ν•΄μ„œ ν•˜λ‚˜μ˜ 식을 ꡬ성할 수 μžˆλŠ” κ²ƒμ²˜λŸΌ, μ—¬λŸ¬ Predicateλ₯Ό and(), or(), negate()둜 μ—°κ²°ν•΄μ„œ ν•˜λ‚˜μ˜ μƒˆλ‘œμš΄ Predicate둜 κ²°ν•©ν•  수 μžˆλ‹€.
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i % 2 == 0;
Predicate<Integer> notP = p.negate(); // i >= 100

// 100 <= i && (i < 200 || i%2 ==0)
Predicate<Integer> all = notP.and(q.or(t));
System.out.println(all.test(150)); // true
  • 이처럼 and(), or(), negate()둜 μ—¬λŸ¬ 쑰건식을 ν•˜λ‚˜λ‘œ ν•©μΉ  수 μžˆλ‹€. λ¬Όλ‘  μ•„λž˜μ™€ 같이 λžŒλ‹€μ‹μ„ 직접 넣어도 λœλ‹€.
Predicate<Integer> all = notP.and(i -> i < 200).or(i -> i % 2 == 0);
  • 주의) Predicate의 끝에 negate()λ₯Ό 뢙이면 쑰건식 전체가 뢀정이 λœλ‹€.
  • 그리고 static λ©”μ„œλ“œμΈ isEqual()은 두 λŒ€μƒμ„ λΉ„κ΅ν•˜λŠ” Predicateλ₯Ό λ§Œλ“€ λ•Œ μ‚¬μš©ν•œλ‹€. λ¨Όμ € isEqual()의 λ§€κ°œλ³€μˆ˜λ‘œ λΉ„κ΅λŒ€μƒμ„ ν•˜λ‚˜ μ§€μ •ν•˜κ³ , 또 λ‹€λ₯Έ λΉ„κ΅λŒ€μƒμ€ test()의 λ§€κ°œλ³€μˆ˜λ‘œ μ§€μ •ν•œλ‹€.
Predciate<String> p = Predicate.isEqual(str1);
boolean result = p.test(str2); // str1κ³Ό str2κ³Ό 같은지 λΉ„κ΅ν•˜μ—¬ κ²°κ³Όλ₯Ό λ°˜ν™˜
  • μœ„μ˜ 두 λ¬Έμž₯을 ν•©μΉ˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€. 였히렀 μ•„λž˜μ˜ λ¬Έμž₯이 μ΄ν•΄ν•˜κΈ° 더 μ‰¬μšΈ 것이닀.
// str1κ³Ό str2κ°€ 같은지 비ꡐ
boolean result = Predicate.isEqual(str1).test(str2);
  • 예제 14-7)
import java.util.function.Function;
import java.util.function.Predicate;

public class LambdaEx7 {
    public static void main(String[] args) {
        Function<String, Integer> f = (s) -> Integer.parseInt(s, 16);
        Function<Integer, String> g = (i) -> Integer.toBinaryString(i);

        Function<String, String> h = f.andThen(g);
        Function<Integer, Integer> h2 = f.compose(g);

        System.out.println(h.apply("FF")); // "FF" -> 255 -> "11111111"
        System.out.println(h2.apply(2)); // 2 -> "10" -> 16

        Function<String, String> f2 = x -> x; // ν•­λ“±ν•¨μˆ˜(identity function)
        System.out.println(f2.apply("AAA")); // AAAκ°€ κ·ΈλŒ€λ‘œ 좜λ ₯됨

        Predicate<Integer> p = i -> i < 100;
        Predicate<Integer> q = i -> i < 200;
        Predicate<Integer> r = i -> i % 2 == 0;
        Predicate<Integer> notP = p.negate(); // i >= 100

        Predicate<Integer> all = notP.and(q.or(r));
        System.out.println(all.test(150)); // true

        String str1 = "abc";
        String str2 = "abc";

        // str1κ³Ό str2κ°€ 같은지 λΉ„κ΅ν•œ κ²°κ³Όλ₯Ό λ°˜ν™˜
        Predicate<String> p2 = Predicate.isEqual(str1);
        boolean result = p2.test(str2);
        System.out.println(result); // true
    }
}

6. λ©”μ„œλ“œ μ°Έμ‘°

  • λžŒλ‹€μ‹μœΌλ‘œ λ©”μ„œλ“œλ₯Ό 이처럼 κ°„κ²°ν•˜κ²Œ ν‘œν˜„ν•  수 μžˆλŠ” κ²ƒλ§Œ 해도 μž₯점. κ·ΈλŸ¬λ‚˜ λžŒλ‹€μ‹μ„ λ”μš± κ°„κ²°ν•˜κ²Œ ν‘œν˜„ν•  수 μžˆλŠ” 방법이 μž‡λ‹€. 항상 그런 것은 μ•„λ‹ˆκ³ , λžŒλ‹€μ‹μ΄ ν•˜λ‚˜μ˜ λ©”μ„œλ“œλ§Œ ν˜ΈμΆœν•˜λŠ” κ²½μš°μ—λŠ” λ©”μ„œλ“œ μ°Έμ‘°(method reference)λΌλŠ” λ°©λ²•μœΌλ‘œ λžŒλ‹€μ‹μ„ κ°„λž΅νžˆ ν•  수 μžˆλ‹€. 예λ₯Ό λ“€μ–΄ λ¬Έμžμ—΄μ„ μ •μˆ˜λ‘œ λ³€ν™˜ν•˜λŠ” λžŒλ‹€μ‹μ€ μ•„λž˜μ™€ 같이 μž‘μ„±ν•  수 μžˆλ‹€.
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
  • 보톡은 μœ„μ²˜λŸΌ λžŒλ‹€μ‹μ„ μž‘μ„±ν•˜λŠ”λ°, 이 λžŒλ‹€μ‹μ„ λ©”μ„œλ“œλ‘œ ν‘œν˜„ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

    • μ°Έκ³ ) λžŒλ‹€μ‹μ€ μ—„λ°€νžˆ λ§ν•˜μžλ©΄ 읡λͺ…ν΄λž˜μŠ€μ˜ κ°μ²΄μ§€λ§Œ κ°„λ‹¨νžˆ λ©”μ„œλ“œλ§Œ μ μ—ˆλ‹€.

      Integer wrapper(String s) { // 이 λ©”μ„œλ“œμ˜ 이름은 μ˜λ―Έμ—†λ‹€.
      return Integer.parseInt(s);
      }
  • μœ„μ˜ wrapper λ©”μ„œλ“œλŠ” λ³„λ‘œ ν•˜λŠ” 일이 μ—†λ‹€.κ·Έμ € 값을 λ°›μ•„μ„œ Integer.parseInt()μ—κ²Œ λ„˜κ²¨μ£ΌλŠ” 일만 ν•  뿐이닀. 차라리 이 κ±°μΆ”μž₯슀러운 λ©”μ„œλ“œλ₯Ό 벗겨내고 Integer.parseInt()λ₯Ό 직접 ν˜ΈμΆœν•˜λŠ” 것이 더 낫지 μ•Šμ„κΉŒ?

    • 즉 μ•„λž˜λ₯Ό,
    Function<String, Integer> f = (String s) -> Integer.parseInt(s);
    • μ•„λž˜μ™€ 같이 λ³€κ²½
    Function<String, Integer> f = Integer::parseInt; // λ©”μ„œλ“œ μ°Έμ‘°
  • μœ„ λ©”μ„œλ“œ μ°Έμ‘°μ—μ„œ λžŒλ‹€μ‹μ˜ 일뢀가 μƒλž΅λ˜μ—ˆμ§€λ§Œ, μ»΄νŒŒμΌλŸ¬λŠ” μƒλž΅λœ 뢀뢄을 μš°λ³€μ˜ parseIntλ©”μ„œλ“œμ˜ μ„ μ–ΈλΆ€λ‘œλΆ€ν„°, λ˜λŠ” μ’Œλ³€μ˜ FunctionμΈν„°νŽ˜μ΄μŠ€μ— μ§€μ •λœ 지넀릭 νƒ€μž…μœΌλ‘œλΆ€ν„° μ‰½κ²Œ μ•Œμ•„λ‚Ό 수 μžˆλ‹€.
  • ν•œ 가지 예λ₯Ό 더 보자. μ•„λž˜μ˜ λžŒλ‹€μ‹μ„ λ©”μ„œλ“œ 참쑰둜 λ³€κ²½ν•œλ‹€λ©΄, μ–΄λ–»κ²Œ λ˜κ² λŠ”κ°€? λžŒλ‹€μ‹μ—μ„œ μƒλž΅ν•΄λ„ 쒋을 λ§Œν•œ 뢀뢄이 어디인지 ν•œλ²ˆ 생각해 보자.
BiFunction<String, String, Boolean> f = (s1, s2) -> s1.equals(s2);
  • μ°Έμ‘°λ³€μˆ˜ f의 νƒ€μž…λ§Œ 봐도 λžŒλ‹€μ‹μ΄ 두 개의 Stringνƒ€μž…μ˜ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›λŠ”λ‹€λŠ” 것을 μ•Œ 수 μžˆμœΌλ―€λ‘œ, λžŒλ‹€μ‹μ˜ λ§€κ°œλ³€μˆ˜λ“€μ€ 없어도 λœλ‹€. μœ„μ˜ λžŒλ‹€μ‹μ—μ„œ λ§€κ°œλ³€μˆ˜λ“€μ„ μ œκ±°ν•΄μ„œ λ©”μ„œλ“œ 참쑰둜 λ³€κ²½ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

    • 즉, μ•„λž˜μ˜ μ‹μ—μ„œ.
    BiFunction<String, String, Boolean> f = (s1, s2) -> s1.equals(s2);
    • μ•„λž˜μ˜ μ‹μœΌλ‘œ λ³€κ²½.
    BiFunction<String, String, Boolean> f = String::equals; // λ©”μ„œλ“œ μ°Έμ‘°
  • λ§€κ°œλ³€μˆ˜ s1κ³Ό s2을 μƒλž΅ν•΄λ²„λ¦¬κ³  λ‚˜λ©΄ equals만 λ‚¨λŠ”λ°, 두 개의 String을 λ°›μ•„μ„œ Boolean을 λ°˜ν™˜ν•˜λŠ” equalsλΌλŠ” μ΄λ¦„μ˜ λ©”μ„œλ“œλŠ” λ‹€λ₯Έ ν΄λž˜μŠ€μ—λ„ μ‘΄μž¬ν•  μˆ˜λ„ 있기 λ•Œλ¬Έμ— equals μ•žμ— 클래슀 이름은(μ—¬κΈ°μ„œλŠ” String) λ°˜λ“œμ‹œ ν•„μš”ν•˜λ‹€.
  • λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό μ‚¬μš©ν•  수 μžˆλŠ” κ²½μš°κ°€ ν•œ 가지 더 μžˆλŠ”λ°, 이미 μƒμ„±λœ 객체의 λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μ—μ„œ μ‚¬μš©ν•œ κ²½μš°μ—λŠ” 클래슀 이름 λŒ€μ‹  κ·Έ 객체의 μ°Έμ‘°λ³€μˆ˜λ₯Ό μ μ–΄μ€˜μ•Ό ν•œλ‹€.
MyClass obj = new MyClass();
Function<String, Boolean> f = (x) -> obj.equals(x); // λžŒλ‹€μ‹
Function<String, Boolean> f2 = obj::equals; // λ©”μ„œλ“œ μ°Έμ‘°
  • μ§€κΈˆκΉŒμ§€ 3가지 경우의 λ©”μ„œλ“œ 참쑰에 λŒ€ν•΄μ„œ μ•Œμ•„λ΄€λŠ”λ°, μ •λ¦¬ν•˜λ©΄ λ‹€μŒκ³Ό κ°™λ‹€. (λžŒλ‹€μ‹μ„ λ©”μ„œλ“œ 참쑰둜 λ³€ν™˜ν•˜λŠ” 방법)
μ’…λ₯˜ λžŒλ‹€ λ©”μ„œλ“œ μ°Έμ‘°
staticλ©”μ„œλ“œ μ°Έμ‘° (x) -> ClassName.method(x) ClassName::method
μΈμŠ€ν„΄μŠ€λ©”μ„œλ“œ μ°Έμ‘° (obj, x) -> obj.method(x) className::method
νŠΉμ • 객체 μΈμŠ€ν„΄μŠ€λ©”μ„œλ“œ μ°Έμ‘° (x) -> obj.method(x) obj::method
  • 정리

    • ν•˜λ‚˜μ˜ λ©”μ„œλ“œλ§Œ ν˜ΈμΆœν•˜λŠ” λžŒλ‹€μ‹μ€ 'ν΄λž˜μŠ€μ΄λ¦„::λ©”μ„œλ“œμ΄λ¦„' λ˜λŠ” 'μ°Έμ‘°λ³€μˆ˜::λ©”μ„œλ“œμ΄λ¦„'으둜 λ°”κΏ€ 수 μžˆλ‹€.

μƒμ„±μžμ˜ λ©”μ„œλ“œ μ°Έμ‘°

  • μƒμ„±μžλ₯Ό ν˜ΈμΆœν•˜λŠ” λžŒλ‹€μ‹λ„ λ©”μ„œλ“œ 참쑰둜 λ³€ν™˜ν•  수 μžˆλ‹€.
Supplier<MyClass> s = () -> new MyClass(); // λžŒλ‹€μ‹
Supplier<MyClass> s = Myclass:new; // λ©”μ„œλ“œ μ°Έμ‘°
  • λ§€κ°œλ³€μˆ˜κ°€ μžˆλŠ” μƒμ„±μžλΌλ©΄, λ§€κ°œλ³€μˆ˜μ˜ κ°œμˆ˜μ— 따라 μ•Œλ§žμ€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. ν•„μš”ν•˜λ‹€λ©΄ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μƒˆλ‘œ μ •μ˜ν•΄μ•Ό ν•œλ‹€.
Function<Integer, MyClass> f = (i) -> new MyClass(i); // λžŒλ‹€μ‹
Function<Integer, Myclass> f2 = MyClass::new; // λ©”μ„œλ“œ μ°Έμ‘°

BiFunction<Integer, String, MyClass> bf = (i, s) -> new MyClass(i, s);
BiFunction<Integer, String, Myclass> bf2 = MyClass::new; // λ©”μ„œλ“œ μ°Έμ‘°
  • 그리고 배열을 생성할 λ•ŒλŠ” μ•„λž˜μ™€ 같이 ν•˜λ©΄ λœλ‹€.
Function<Integer, int[]> f = x -> new int[x]; // λžŒλ‹€μ‹
Function<Integer, int[]> f2 = int[]::new; // λ©”μ„œλ“œ μ°Έμ‘°
  • λ©”μ„œλ“œ μ°Έμ‘°λŠ” λžŒλ‹€μ‹μ„ 마치 staticλ³€μˆ˜μ²˜λŸΌ λ‹€λ£° 수 있게 ν•΄μ€€λ‹€. λ©”μ„œλ“œ μ°Έμ‘°λŠ” μ½”λ“œλ₯Ό κ°„λž΅νžˆ ν•˜λŠ”λ° μœ μš©ν•΄μ„œ 많이 μ‚¬μš©λœλ‹€. λžŒλ‹€μ‹μ„ λ©”μ„œλ“œ 참쑰둜 λ³€ν™˜ν•˜λŠ” μ—°μŠ΅μ„ λ§Žμ΄ν•΄μ„œ 빨리 μ΅μˆ™ν•΄μ§€κΈ° λ°”λž€λ‹€.
  • ν…ŒμŠ€νŠΈ

Written by@[June]
Backend Developer