Java

(지피티 선생님의) InvokeDynamic 특강

99duuk 2024. 12. 12. 14:42

 

 

(지피티 선생님의) 람다와 일급 객체, FP 특강

🍱  말밥: 람다와 일급 객체람다를 이렇게 생각해봐. ‘코드를 바로 손에 들고 쓸 수 있는 작은 조각’ 같은 거야. 한 번, 이런 상상을 해보자:🍳 요리사 이야기요리사가 있는데, 이 사람은 특

99duuk.tistory.com

이어서!!!

 


🍱 말밥: 인보크 다이나믹이 뭐야?

자, 인보크 다이나믹(InvokeDynamic)은 자바의 "마술 도구" 같은 거야.
평소에 우리가 코드를 실행할 때, 자바는 "야, 이 메서드 찾아봐!" 하면서 메서드 호출 경로를 딱딱하게 정해둬.
그런데 인보크 다이나믹은 이렇게 고정된 호출 경로를 완전히 유연하게 만들어줘.


🍳 비유: 요리사의 비법

보통 요리사가 요리를 만들려면:

  1. **레시피(코드)**를 꺼내고,
  2. 그걸 따라 만들어야 해.
    즉, 어떤 요리를 만들지는 처음부터 정해져 있는 거야.

하지만 인보크 다이나믹을 쓰면?

  • 요리사가 즉석에서 레시피를 고를 수 있는 도구를 가진 것과 같아.
  • "이 상황에 맞게 딱 알맞은 레시피를 찾아서 써!"라고 말할 수 있지.

이렇게 하면:

  • **동적으로 적합한 레시피(메서드)**를 골라 쓰는 거라, 코드를 훨씬 유연하게 작성할 수 있어.

🛠️ 인보크 다이나믹이 실제로 뭐하는 건데?

  • 자바의 invokedynamic 명령어는 자바 바이트코드에서 제공하는 기능으로, 동적 메서드 호출을 가능하게 만들어.
  • 이를 통해 컴파일 타임에 고정된 메서드 호출 대신 런타임에 메서드를 결정할 수 있어.

🎩 어디에 쓰이는데?

  1. 람다 표현식
    • 자바 8부터 람다 표현식을 지원하면서, 내부적으로 인보크 다이나믹을 활용해 람다를 효율적으로 구현했어.
    • 과거에는 람다를 익명 클래스 형태로 구현했는데, 인보크 다이나믹 덕분에 더 가벼운 구현이 가능해짐.
  2. 동적 언어 지원 (JSR 292)
    • 자바는 정적 타입 언어지만, JavaScript, Groovy, Kotlin 같은 동적 언어를 실행할 때도 사용돼.
    • 동적 언어에서 "어떤 메서드를 호출할지" 런타임에 결정하는 데 인보크 다이나믹을 활용.
  3. 메서드 핸들(MethodHandle)
    • 메서드 핸들은 Method 객체처럼 동적으로 메서드를 호출할 수 있는 자바의 유연한 도구야.
    • invokedynamic 명령어와 함께 동작하며, 유연한 메서드 호출을 가능하게 해.

🛠️ 자바 코드 예제: 인보크 다이나믹과 람다

람다의 내부 동작을 통해 인보크 다이나믹을 엿볼 수 있어.

 

import java.util.function.Function;

public class LambdaExample {
    public static void main(String[] args) {
        Function<Integer, Integer> square = x -> x * x; // 람다 표현식

        System.out.println(square.apply(5)); // 25
    }
}

이 코드 내부적으로

  • 람다 표현식은 인보크 다이나믹(invokedynamic) 명령어를 사용해서 처리돼.
  • invokedynamic은 런타임에 필요한 구현체(레시피)를 동적으로 찾아서 호출해.
  • 이렇게 하면 메모리 효율과 실행 속도가 크게 개선돼!

🛠️ 동적 메서드 호출 예제: MethodHandle

직접 메서드 핸들과 인보크 다이나믹을 사용한 동적 호출을 구현해보자

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class InvokeDynamicExample {
    public static void main(String[] args) throws Throwable {
        // 메서드 핸들 생성
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType methodType = MethodType.methodType(String.class, String.class);

        // Example 클래스의 sayHello 메서드 핸들 가져오기
        MethodHandle handle = lookup.findStatic(Example.class, "sayHello", methodType);

        // 메서드 핸들 호출 (동적으로!)
        String result = (String) handle.invokeExact("World");
        System.out.println(result); // Hello, World
    }
}

class Example {
    public static String sayHello(String name) {
        return "Hello, " + name;
    }
}

MethodHandles.Lookup lookup = MethodHandles.lookup();

  • MethodHandles.Lookup 객체는 메서드 핸들을 조회하는 데 필요한 도구를 제공해.
  • lookup() 메서드를 통해 현재 클래스에서 접근 가능한 메서드나 필드의 핸들을 찾을 수 있어.
  • 즉, 여기서는 Example 클래스의 메서드를 동적으로 호출하기 위해 이 도구를 사용.

MethodType methodType = MethodType.methodType(String.class, String.class);

  • MethodType은 호출하려는 메서드의 **시그니처(반환 타입과 파라미터 타입)**를 나타내.
  • 여기서 String.class는 반환 타입이 String, 매개변수 타입도 String임을 나타냄.
  • 즉, sayHello(String) 메서드의 시그니처를 정의한 것.

MethodHandle handle = lookup.findStatic(Example.class, "sayHello", methodType);

  • findStatic은 정적 메서드를 위한 핸들을 찾아 반환.
    • 첫 번째 인자: 호출할 메서드가 속한 클래스 (Example.class).
    • 두 번째 인자: 호출할 메서드의 이름 ("sayHello").
    • 세 번째 인자: 메서드의 시그니처(methodType).
  • 이 라인을 통해 sayHello(String) 메서드의 핸들(참조)을 가져옴.

 

String result = (String) handle.invokeExact("World");

  • invokeExact는 메서드 핸들을 사용해 메서드를 호출하는 방법.
  • "World"는 sayHello 메서드의 매개변수로 전달되는 값.
  • 이 호출은 Example.sayHello("World")와 동일하게 작동하지만, 핸들을 통해 동적으로 호출했기 때문에 컴파일 타임에 연결된 것이 아니라 런타임에 결정된 메서드를 호출한 것.
  •  

💡 설명:

  • MethodHandle은 메서드 호출을 다루는 도구로, 런타임에 동적으로 메서드를 호출할 수 있어.
  • invokedynamic 명령어가 이 동작을 지원하며, 정적인 호출 방식보다 더 유연하게 동작.

🚀 정리: FP, 람다, 일급 객체와 인보크 다이나믹

  1. 일급 객체:
    • 함수처럼 데이터를 자유롭게 다루는 개념.
  2. 람다:
    • 자바에서 함수를 일급 객체처럼 활용하기 위한 방법.
    • 인보크 다이나믹 덕분에 효율적으로 구현.
  3. FP(함수형 프로그래밍):
    • 함수 조합이 핵심이고, 람다일급 객체가 기반.
  4. 인보크 다이나믹:
    • 자바의 바이트코드 수준에서 동적 메서드 호출을 지원.
    • 람다, 동적 언어, 유연한 메서드 호출의 핵심 기술.

💡 핵심 요약

"람다와 FP가 쉽게 쓰일 수 있는 건, 인보크 다이나믹이라는 도구가 있어서 가능하다!"