`StringConcatFactory`中的方法`makeConcat `和`makeConcatWithConstants`如何直接调用API使用?

How can the methods `makeConcat​` and `makeConcatWithConstants` in `StringConcatFactory` used by directly calling the API?

我相信因为 Java 9 字符串连接已经使用 StringConcatFactory 实现了。

因为这是作为 API 在 Java 中提供的,所以 StringConcatFactory 中的方法 makeConcat​makeConcatWithConstants 如何通过直接调用 API?到目前为止,我找不到任何使用它的不同方式的例子。另外,makeConcat​makeConcatWithConstants中的参数String nameMethodType concatTypemakeConcatWithConstants中的参数String recipeObject... constants是什么意思,什么意思应该从Java Docs.

传递给他们对我来说不是不言而喻的

您链接的文档指出,“这些方法通常用作 bootstrap 方法......以支持字符串连接功能”,这意味着,编译器使用它们来准备执行 String连接 - 换句话说,不打算供程序员使用(我记得,他们基本上创建了一个(一种 lambda)方法来调用连接)。

使用它的简单方法:+ 运算符连接字符串并让编译器完成这项工作。如果你真的想直接使用那些方法(为什么?这不是那么简单),使用连接创建一个 class 并用一些反编译器检查生成的 javacode。

另见:(不是100%相关,而是最后一句)

您不应该直接调用此 API。 class 旨在为 invokedynamic 指令提供 bootstrap 方法,因此它的 API 对于该用例是直接的,但不是直接调用。

但是 the documentation 是详尽无遗的:

Parameters

  • lookup - Represents a lookup context with the accessibility privileges of the caller. When used with invokedynamic, this is stacked automatically by the VM.
  • name - The name of the method to implement. This name is arbitrary, and has no meaning for this linkage method. When used with invokedynamic, this is provided by the NameAndType of the InvokeDynamic structure and is stacked automatically by the VM.
  • concatType - The expected signature of the CallSite. The parameter types represent the types of concatenation arguments; the return type is always assignable from String. When used with invokedynamic, this is provided by the NameAndType of the InvokeDynamic structure and is stacked automatically by the VM.

强调是我加的

请注意 JVM 通常如何根据 invokedynamic 字节码指令自动提供所有参数。在这种情况下,它是一条使用一些参数并生成 String 的单个指令,将此 bootstrap 方法称为知道如何执行操作的实体。

当您想手动调用它时,无论出于何种原因,您都必须执行类似

的操作
String arg1 = "Hello";
char arg2 = ' ';
String arg3 = "StringConcatFactory";

MethodHandle mh = StringConcatFactory.makeConcat(
    MethodHandles.lookup(), // normally provided by the JVM
    "foobar", // normally provided by javac, but meaningless here
    // method type is normally provided by the JVM and matches the invocation
    MethodType.methodType(String.class, String.class, char.class, String.class))
    .getTarget();

// we can now use the handle to perform a concatenation
// the argument types must match the MethodType specified above
String result = (String)mh.invokeExact(arg1, arg2, arg3);

System.out.println(result);

您可以重复使用 MethodHandle 进行多个字符串连接,但您必须绑定到您在 bootstrapping 期间指定的参数类型。

对于普通的字符串连接表达式,每个表达式在其 bootstrapping 期间都链接到一个句柄,该句柄匹配固定数量的子表达式及其编译时类型。

很难想象直接使用 API 比只写 arg1 + arg2 + arg3 等有好处的场景


makeConcatWithConstants bootstrap 方法允许指定常量部分以及可能变化的参数。例如,当我们有代码

String time = switch(LocalTime.now().get(ChronoField.HOUR_OF_DAY) / 6) {
    case 0 -> "night"; case 1 -> "morning"; case 2 -> "afternoon";
    case 3 -> "evening"; default -> throw new AssertionError();                
};

System.out.println("Hello "+System.getProperty("user.name")+", good "+time+"!");

我们有几个常量部分,编译器可以将它们合并为一个字符串,使用占位符 </code> 表示必须插入动态值的位置,因此 <code>recipe 参数将是 "Hello , good !"。另一个参数 constants 将不被使用。那么对应的invokedynamic指令只需要提供操作数栈上的两个动态值即可。

为了使等效的手动调用更有趣,我们假设系统 属性 user.name 是不变的,因此我们可以在 bootstrap 调用中将其作为常量提供,使用占位符 </code> 来引用它,并产生一个只消耗一个动态参数的句柄,时间字符串:</p> <pre><code>MethodHandle mh = StringConcatFactory.makeConcatWithConstants( MethodHandles.lookup(), // normally provided by the JVM "foobar", // normally provided by javac, but meaningless here // method type is normally provided by the JVM and matches the invocation MethodType.methodType(String.class, String.class), "Hello , good !", // recipe, binds a parameter, a constant System.getProperty("user.name") // the first constant to bind ).getTarget(); // we can now use the handle to perform a concatenation // the argument types must match the MethodType specified above String result = (String)mh.invokeExact(time); System.out.println(result);

普通 Java 代码很少会使用额外的 constants。我所知道的唯一情况是在原始常量字符串中包含 </code> 或 <code> 的极端情况。为了防止它们被解释为占位符,这些子字符串将作为常量提供。

this online code tester 所示,代码

String time = switch(LocalTime.now().get(ChronoField.HOUR_OF_DAY) / 6) {
    case 0 -> "night"; case 1 -> "morning"; case 2 -> "afternoon";
    case 3 -> "evening"; default -> throw new AssertionError();                
};

System.out.println("Hello "+System.getProperty("user.name")+", good "+time+"!");

String tmp = "prefix  "+time+"  suffix";

编译为(省略无关部分):

 0: invokestatic  #1                  // Method java/time/LocalTime.now:()Ljava/time/LocalTime;
 3: getstatic     #7                  // Field java/time/temporal/ChronoField.HOUR_OF_DAY:Ljava/time/temporal/ChronoField;
 6: invokevirtual #13                 // Method java/time/LocalTime.get:(Ljava/time/temporal/TemporalField;)I
 9: bipush        6
11: idiv
12: tableswitch   { // 0 to 3
               0: 44
               1: 49
               2: 54
               3: 59
         default: 64
    }
44: ldc           #17                 // String night
46: goto          72
49: ldc           #19                 // String morning
51: goto          72
54: ldc           #21                 // String afternoon
56: goto          72
59: ldc           #23                 // String evening
61: goto          72
64: new           #25                 // class java/lang/AssertionError
67: dup
68: invokespecial #27                 // Method java/lang/AssertionError."<init>":()V
71: athrow
72: astore_1
73: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
76: ldc           #37                 // String user.name
78: invokestatic  #39                 // Method java/lang/System.getProperty:(Ljava/lang/String;)Ljava/lang/String;
81: aload_1
82: invokedynamic #43,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
87: invokevirtual #47                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
90: aload_1
91: invokedynamic #53,  0             // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
96: astore_2
BootstrapMethods:
  0: #150 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #151 Hello \u0001, good \u0001!
  1: #150 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #153 \u0002\u0001\u0002
      #155 prefix \u0001
      #157  \u0002 suffix