Language/Java

β˜• λžŒλ‹€μ‹μ„ 더 짧게 - λ©”μ†Œλ“œ::μ°Έμ‘° 문법

인파_ 2023. 3. 30. 09:52

lambda-Method-Reference

λžŒλ‹€μ‹ λ©”μ†Œλ“œ::μ°Έμ‘°

μžλ°”μ˜ λžŒλ‹€ν‘œν˜„μ‹μ„ 톡해 μ½”λ“œ μ •μ˜λ₯Ό ν˜μ‹ μ μœΌλ‘œ μ€„μ—¬μ£Όμ—ˆμ§€λ§Œ 이보닀 더 κ°„λž΅ν•˜κ²Œ μ€„μ΄λŠ” 문법이 μžˆλ‹€.

λ©”μ†Œλ“œ μ°Έμ‘°(Method Reference)λŠ” 말 κ·ΈλŒ€λ‘œ μ‹€ν–‰ν•˜λ €λŠ” λ©”μ†Œλ“œλ₯Ό μ°Έμ‘°ν•΄μ„œ 맀개 λ³€μˆ˜μ˜ 정보 및 리턴 νƒ€μž…μ„ μ•Œμ•„λ‚΄μ–΄, λžŒλ‹€μ‹μ—μ„œ ꡳ이 선언이 λΆˆν•„μš”ν•œ 뢀뢄을 μƒλž΅ν•˜λŠ” 것을 λ§ν•œλ‹€.

λžŒλ‹€μ‹μ€ λ³΅μž‘ν•˜κ³  κΈΈλ‹€λž€ λ‘œμ§λ³΄λ‹¨ κΈ°μ‘΄ λ©”μ†Œλ“œλ“€μ„ λ‹¨μˆœνžˆ 호좜만 ν•˜λŠ” κ²½μš°κ°€ λ§Žλ‹€. 예λ₯Ό λ“€μ–΄ 두 개의 값을 λ°›μ•„ μž‘μ€ 수λ₯Ό λ¦¬ν„΄ν•˜λŠ” Math 클래슀의 max() 정적 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” λžŒλ‹€μ‹μ€ λ‹€μŒκ³Ό κ°™λ‹€.

(x, y) -> Math.max(x, y)

이 μžμ²΄λ‘œλ„ κ°„λž΅ν•΄ λ³΄μ΄μ§€λ§Œ, μžλ°” κ°œλ°œμ§„μ€ κ·ΈλŸ¬μ§€ μ•Šμ€κ°€ 보닀. ν•¨μˆ˜ ν˜•νƒœλ₯Ό 보면 리턴값 μžμ²΄κ°€ λ˜λ‹€λ₯Έ Math 클래슀의 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” 것 뿐이고, λžŒλ‹€ ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜ μ—­μ‹œ κ·ΈλŒ€λ‘œ max() λ©”μ„œλ“œμ˜ λ§€κ°œλ³€μˆ˜λ‘œ λ“€μ–΄κ°€κΈ° λ•Œλ¬Έμ— μ½”λ“œ 쀑볡이 λ°œμƒν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

lambda-Method-Reference

λ”°λΌμ„œ μ€‘λ³΅λ˜λŠ” λ§€κ°œλ³€μˆ˜λ₯Ό μ—†μ• κ³ , ν™”μ‚΄ν‘œλ₯Ό μ—†μ• κ³ , ν΄λž˜μŠ€κ°€ λ©”μ†Œλ“œλ₯Ό μ°Έμ‘°ν•˜λŠ” 기호인 . 기호λ₯Ό :: 기호둜 λ³€ν™˜ν•˜λ©΄ λ‹€μŒκ³Ό 같이 ν‘œν˜„ ν•  수 있게 λœλ‹€.

Math::max; // (x, y) -> Math.max(x, y)

λ©”μ„œλ“œ μ°Έμ‘° 쑰건과 νƒ€μž… μΆ”λ‘ 

λ§Œμ•½ Math::min λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό λ³€μˆ˜μ— λ‹΄κ³  μ‹Άλ‹€λ©΄, ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μΈ IntBinaryOperator μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. 두 개의 int ν˜• μž…λ ₯값을 λ°›μ•„ int 값을 λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ λ””μŠ€ν¬λ¦½ν„°λ₯Ό κ°–κ³  있기 λ•Œλ¬Έμ΄λ‹€.

lambda-Method-Reference

IntBinaryOperator b = Math::min;
b.applyAsInt(100, 200); // 100

μ΄λŸ°μ‹μœΌλ‘œ 문법 함좕이 κ°€λŠ₯ν•œ μ΄μœ λŠ” μ»΄νŒŒμΌλŸ¬κ°€ λžŒλ‹€μ‹μ˜ νƒ€μž…μ„ μΆ”λ‘ ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. μ΄λ•Œ λžŒλ‹€ νƒ€μž… 좔둠은 기쑴의 λžŒλ‹€ 읡λͺ… ν•¨μˆ˜μ™€λŠ” μ•½κ°„ 차이가 μžˆλ‹€. 였히렀 더 κ°„λ‹¨ν•œλ°, μΈν„°νŽ˜μ΄μŠ€μ˜ 좔상 λ©”μ„œλ“œ ν˜•νƒœμ™€ λ°˜ν™˜ λ©”μ„œλ“œμ˜ μ‹œκ·Έλ‹ˆμ²˜ ν˜•νƒœκ°€ κ°™μœΌλ©΄ λœλ‹€.

lambda-Method-Reference

μœ„μ˜ κ·Έλ¦Όκ³Ό 같이, IntBinaryOperator μΈν„°νŽ˜μ΄μŠ€μ˜ 좔상 λ©”μ„œλ“œλŠ” intν˜• λ§€κ°œλ³€μˆ˜ 2개λ₯Ό λ°›μœΌλ©° λ°˜ν™˜ νƒ€μž…μ΄ intν˜•μ΄κ³ , Math 클래슀의 max λ©”μ„œλ“œ μ—­μ‹œ νƒ€μž…μ΄ λ˜‘κ°™μ΄ κ΅¬μ„±λ˜μ–΄ 있기 λ•Œλ¬Έμ—, 이λ₯Ό μ»΄νŒŒμΌλŸ¬κ°€ μΆ”λ‘ ν•΄μ„œ κ°€λŠ₯ν•œ 것이닀.

μ •λ¦¬ν•˜μžλ©΄, λžŒλ‹€μ‹μ˜ λ©”μ†Œλ“œ μ°Έμ‘° 문법을 μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ‹€μŒμ˜ 3가지 쑰건을 λ§Œμ‘±ν•˜λ©΄ λœλ‹€.

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ λ§€κ°œλ³€μˆ˜ νƒ€μž… == λ©”μ†Œλ“œμ˜ λ§€κ°œλ³€μˆ˜ νƒ€μž…
  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ λ§€κ°œλ³€μˆ˜ 개수 == λ©”μ†Œλ“œμ˜ λ§€κ°œλ³€μˆ˜ 개수
  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ λ°˜ν™˜ νƒ€μž… == λ©”μ†Œλ“œμ˜ λ°˜ν™˜ νƒ€μž…

λ©”μ„œλ“œ μ°Έμ‘° μ’…λ₯˜

μ–΄λ– ν•œ λ©”μ„œλ“œλ₯Ό μ°Έμ‘°ν•˜μ—¬ μ‹€ν–‰ν•˜λŠλƒμ— λ©”μ†Œλ“œ 참쑰도 μ’…λ₯˜κ°€ λ‚˜λ‰˜κ²Œ λœλ‹€.

μ’…λ₯˜ λžŒλ‹€ ν‘œν˜„μ‹ λ©”μ„œλ“œ μ°Έμ‘°
정적 λ©”μ„œλ“œ μ°Έμ‘° (x) -> ClassName.method(x) ClassName::method
μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œ μ°Έμ‘° (x) -> obj.method(x) obj::method
λ§€κ°œλ³€μˆ˜μ˜ λ©”μ„œλ“œ μ°Έμ‘° (obj, x) -> obj.method(x) ClassName::method
μƒμ„±μž μ°Έμ‘° (x, y) -> new ClassName(x, y) ClassName::new

 

정적 λ©”μ†Œλ“œ μ°Έμ‘°

λ‹€μŒμ€ Interger 클래슀의 parseInt() 정적 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” λžŒλ‹€μ‹μ„ λ©”μ†Œλ“œ 참쑰둜 λ¦¬νŒ©ν† λ§ν•œ μ˜ˆμ œμ΄λ‹€.

정적 λ©”μ†Œλ“œλ₯Ό μ°Έμ‘°ν• λ•Œ, λ©”μ„œλ“œ μ°Έμ‘° :: 기호 μ•žλΆ€λΆ„μ— 클래슀λͺ…을 κ·ΈλŒ€λ‘œ κΈ°μž¬ν•˜λŠ” 것이 νŠΉμ§•μ΄λ‹€.

Function<String, Integer> stringToInt;

// (x) -> ClassName.method(x)
stringToInt = (s) -> Integer.parseInt(s);

// ClassName::method
stringToInt = Integer::parseInt;

stringToInt.apply("100");

 

μΈμŠ€ν„΄μŠ€ λ©”μ†Œλ“œ μ°Έμ‘°

λ‹€μŒμ€ ArrayList 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κ³  인자둜 Collection을 λ°›μ•„ λ¦¬μŠ€νŠΈμ— μš”μ†Œλ“€μ„ μΆ”κ°€ν•˜λŠ” λžŒλ‹€μ‹μ„ λ©”μ†Œλ“œ 참쑰둜 λ¦¬νŒ©ν† λ§ν•œ μ˜ˆμ œμ΄λ‹€.

μΈμŠ€ν„΄μŠ€μ˜ λ©”μ†Œλ“œλ₯Ό μ°Έμ‘°ν• λ•Œ, λ©”μ„œλ“œ μ°Έμ‘° :: κΈ°ν˜Έ μ•žλΆ€λΆ„μ— 상단에 μ„ μ–Έν•œ μΈμŠ€ν„΄μŠ€ λ³€μˆ˜λ₯Ό κΈ°μž¬ν•˜λŠ” 것이 νŠΉμ§•μ΄λ‹€.

ArrayList<Number> list = new ArrayList<>();
Consumer<Collection<Number>> addElements;

// (x) -> obj.method(x)
addElements = (arr) -> list.addAll(arr);

// obj::method
addElements = list::addAll;

addElements.accept(List.of(1, 2, 3, 4, 5));

System.out.println(list); // [1, 2, 3, 4, 5]

 

λ§€κ°œλ³€μˆ˜μ˜ λ©”μ„œλ“œ μ°Έμ‘°

λ‹€μŒμ€ String 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό λ°›μ•„ λ¬Έμžμ—΄μ˜ 길이λ₯Ό κ΅¬ν•˜λŠ” λžŒλ‹€μ‹μ„ λ©”μ†Œλ“œ 참쑰둜 λ¦¬νŒ©ν† λ§ν•œ μ˜ˆμ œμ΄λ‹€.

λ§€κ°œλ³€μˆ˜μ˜ λ©”μ†Œλ“œλ₯Ό μ°Έμ‘°ν• λ•Œ, λ©”μ„œλ“œ μ°Έμ‘° :: κΈ°ν˜Έ μ•žλΆ€λΆ„μ— 정적 λ©”μ†Œλ“œ 참쑰와 λ˜‘κ°™μ΄ λ§€κ°œλ³€μˆ˜μ˜ 클래슀 νƒ€μž…λͺ…을 κΈ°μž¬ν•˜λŠ” 것이 νŠΉμ§•μ΄λ‹€.

λ§€κ°œλ³€μˆ˜μ˜ λ©”μ„œλ“œ μ°Έμ‘°

Function<String, Integer> size;

// (obj, x) -> obj.method(x)  
size = (String s1) ->  s1.length();

// ClassName::method
size = String::length;

size.apply("Hello World"); // 11

 

μƒμ„±μž μ°Έμ‘°

μƒμ„±μžλ„ μΌμ’…μ˜ λ©”μ†Œλ“œ 이기 λ•Œλ¬Έμ— λ©”μ„œλ“œ μ°Έμ‘°κ°€ κ°€λŠ₯ν•˜λ‹€. 리턴값이 λ‹¨μˆœνžˆ 객체λ₯Ό μƒμ„±ν•˜λŠ” 것이기 λ•Œλ¬Έμ— 적용이 κ°€λŠ₯ν•œ 것이닀. λŒ€μ‹  μƒμ„±μžλŠ” 고유 λ©”μ„œλ“œλͺ…이 μ—†κΈ° λ•Œλ¬Έμ— new 둜 ν‘œμ‹œν•œλ‹€.

BiFunction<Integer, Integer, Object> constructor;

// (x, y) -> new ClassName(x, y)  
constructor = (x, y) -> new Object(x, y);

// ClassName::new
constructor = Object::new;

 

μ΄λ•Œ μƒμ„±μž μ˜€λ²„λ‘œλ”©μ„ 톡해 맀개 λ³€μˆ˜ κ°œμˆ˜μ— λ”°λΌμ„œ μΈμŠ€ν„΄μŠ€ν™”λ₯Ό κ΅¬ν˜„ν•˜λ €κ³  ν• λ•Œ, λžŒλ‹€μ‹μ„ 각기 λ‹€λ₯Έ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ— λŒ€μž…ν•˜μ—¬ μ‹€ν–‰μ‹œμΌœμ•Ό ν•œλ‹€.

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

class Customer {
    String name;
    int age;

    public Customer() {
    }

    public Customer(String name) {
        this.name = name;
    }

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public static void main(String[] args) {
    // μƒμ„±μž 인자 없이 μƒμ„±ν• λ•Œ
    Supplier<Customer> function1 = Customer::new;
    Customer customer1 = function1.get();

    // μƒμ„±μž 인자 1개둜만 μƒμ„±ν• λ•Œ (μž…λ ₯νƒ€μž…, μƒμ„±μžν΄λž˜μŠ€)
    Function<String, Customer> function2 = Customer::new;
    Customer customer2 = function2.apply("홍길동");

    // μƒμ„±μž 인자 2개둜만 μƒμ„±ν• λ•Œ (μž…λ ₯νƒ€μž…1, μž…λ ₯νƒ€μž…2, μƒμ„±μžν΄λž˜μŠ€)
    BiFunction<String, Integer, Customer> function3 = Customer::new;
    Customer customer3 = function3.apply("홍길동", 55);
}

 

이λ₯Ό κ³ κΈ‰ μ‘μš©ν•˜μžλ©΄ Map μ»¬λ ‰μ…˜μ— μƒμ„±μž λžŒλ‹€λ₯Ό λ„£μ–΄μ„œ μΈμŠ€ν„΄μŠ€ 관리λ₯Ό 톡해 '지연 μΈμŠ€ν„΄μŠ€ν™”' 기법을 κ΅¬ν˜„ν•  수 μžˆλ‹€. 이 기법은 λ‹€μ΄λ‚˜λ―Ή νŒ©ν† λ¦¬ νŒ¨ν„΄μœΌλ‘œμ„œ λ¦¬ν”Œλ ‰μ…˜ λŒ€μ•ˆμœΌλ‘œ μ‘μš©μ΄ κ°€λŠ₯ν•˜λ‹€.

class Fruit {}

class Apple extends Fruit {
    int count;

    public Apple(int count) {
        this.count = count;
    }
}
class Banana extends Fruit {
    int count;

    public Banana(int count) {
        this.count = count;
    }
}

class Constructor {
    public static Map<String, Function<Integer, Fruit>> map = new HashMap<>();

    static {
        map.put("apple", Apple::new);
        map.put("banana", Banana::new);
    }

    public static Fruit makeFruit(String name, int count) {
        if(map.containsKey(name)) {
            return map.get(name.toLowerCase()).apply(count);
        }
        return null;
    }
}
public static void main(String[] args) {
    Apple apples = (Apple) Constructor.makeFruit("apple", 10);
    Banana bananas = (Banana) Constructor.makeFruit("banana", 4);
}

뢀둝) μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λ©”μ†Œλ“œ μ°Έμ‘°

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ ν™”μ‚΄ν‘œ ν•¨μˆ˜λ„ 사싀 λžŒλ‹€ ν‘œν˜„μ‹μ˜ 일쒅이닀. κ·Έλž˜μ„œ μžλ°”μ™€ 같이 ν™”μ‚΄ν‘œ ν•¨μˆ˜μ— λŒ€ν•œ λ©”μ„œλ“œ μ°Έμ‘°κ°€ κ°€λŠ₯ν•˜λ‹€.

List<Number> list = Arrays.asList(1, 2, 3, 4, 5, 6);

// κΈ°μ‘΄ λžŒλ‹€ 방식
list.forEach( (e) -> System.out.println(e) );

// λ©”μ†Œλ“œ μ°Έμ‘° 방식
list.forEach(System.out::println);
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// κΈ°μ‘΄ 콜백 방식
arr.forEach( (e) => console.log(e) );

// λ©”μ†Œλ“œ μ°Έμ‘° 방식
arr.forEach(console.log);

java-javascript-lambda

그런데 사싀 이 뢀뢄은 λ©”μ„œλ“œ μ°Έμ‘° 같은 고급진 κΈ°λŠ₯이 μ•„λ‹ˆλΌ, μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ ν•¨μˆ˜ 선언식 자체λ₯Ό λ„˜κ²Όμ„ 뿐이닀.

μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ λΈŒλΌμš°μ €μ˜ 이벀트λ₯Ό λ“±λ‘ν• λ•Œ λ‹€μŒκ³Ό 같이 addEventListener의 ν•Έλ“€λŸ¬λ‘œ λ°”λ‘œ ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό μ •μ˜ν•΄μ€˜λ„ λ˜μ§€λ§Œ ν•œλ‹¨κ³„ 거쳐 λ¨Όμ € ν•¨μˆ˜λ₯Ό μ„ μ–Έν•˜κ³  ν•¨μˆ˜ 자체λ₯Ό λ„˜κ²¨μ€˜λ³Έ κ²½ν—˜μ΄ μžˆμ„ 것이닀.

const handler = () => {};

window.addEventListener('click', handler);

addEventListener λ©”μ„œλ“œκ°€ λ§€κ°œλ³€μˆ˜λ‘œ callback ν•¨μˆ˜λ₯Ό λ°›μ•„ λ‚΄λΆ€μ—μ„œ μ‹€ν–‰μ‹œμΌœμ£ΌκΈ° λ•Œλ¬Έμ— μ΄λŸ¬ν•œ μˆ˜μ‹μ΄ κ°€λŠ₯ν•œ 것이닀.

 

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ forEach κ΅¬ν˜„

κ·Έλž˜μ„œ forEachλ₯Ό κ°„λ‹¨ν•˜κ²Œ 직접 κ΅¬ν˜„ν•΄λ³΄λ©΄ μ•„λž˜μ™€ 같이 될 것이닀. μžλ°”μŠ€ν¬λ¦½νŠΈ ν•¨μˆ˜ λ§€κ°œλ³€μˆ˜λ‘œ ν•¨μˆ˜ 자체λ₯Ό λ°›μ•„ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ λ§€κ°œλ³€μˆ˜λ₯Ό callback() κ΄„ν˜Έλ₯Ό λΆ™μ—¬ ν•¨μˆ˜ ν˜ΈμΆœμ„ ν•΄μ£ΌλŠ” 것이닀.

function forEach(arr, callback) {
    for(const i of arr) {
        callback(i); // console.log(i)
    }
}

const arr = [0, 1, 2, 3, 4, 5];
forEach(arr, console.log);

java-javascript-lambda

 

μžλ°”μ˜ forEach κ΅¬ν˜„

μ΄λŸ¬ν•œ 원리가 κ³ λŒ€λ‘œ μžλ°”μ˜ λžŒλ‹€μ—μ„œλ„ μ μš©λœλ‹€λΌκ³  μƒκ°ν•˜λ©΄ λœλ‹€. νŠΉμ • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μœΌλ‘œ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›κ³  λ§€κ°œλ³€μˆ˜μ˜ 좔상 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•¨μœΌλ‘œμ¨ 콜백이 μ‹€ν–‰λ˜κ²Œ λ˜λŠ” 것이닀.

λ‹€μŒμ€ forEach λ™μž‘μ„ μžλ°”λ‘œ μ•„μ£Ό κ°„λ‹¨ν•˜κ²Œ κ΅¬ν˜„ν•΄λ³Έ μ½”λ“œμ΄λ‹€.

public static void forEach(int[] arr, Consumer<? super Number> callback) {
    for (int i : arr) {
        callback.accept(i); // System.out.println(i)
    }
}

public static void main(String[] args) {
    int[] arr = {1,2,3,4,5};
    forEach(arr, System.out::println);
}

java-javascript-lambda


# 참고자료

https://dev-kani.tistory.com/38

https://steady-coding.tistory.com/307