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

Sum*_*ena 2 java string dynamic string-concatenation invokedynamic

我相信自从Java 9字符串连接是使用StringConcatFactory.

\n

由于这是在 Java 中作为 API 提供的,如何通过直接调用 API 来使用这些方法和方法makeConcat\xe2\x80\x8b?到目前为止,我找不到任何使用它的不同方法的示例。另外,参数、in和参数, in 的含义以及应该传递给它们的内容对于我来说在Java 文档中并不是不言自明的。makeConcatWithConstantsStringConcatFactoryString nameMethodType concatTypemakeConcat\xe2\x80\x8bmakeConcatWithConstantsString recipeObject... constantsmakeConcatWithConstants

\n

Hol*_*ger 7

您不应该直接调用此 API。该类旨在为invokedynamic指令提供引导方法,因此其 API 对于该用例来说是直接的,但不适用于直接调用。

\n

文档很详尽:

\n
\n

参数

\n
    \n
  • lookup- 表示具有调用者可访问权限的查找上下文。当与 一起使用时invokedynamic,它会由 VM 自动堆叠。
  • \n
  • name- 要实现的方法的名称。该名称是任意的,对于该链接方法没有任何意义。当与 一起使用时,它由结构的invokedynamic提供,并由 VM 自动堆叠。NameAndTypeInvokeDynamic
  • \n
  • concatType- CallSite 的预期签名。参数类型表示串联参数的类型;返回类型始终可以从 分配String。当与 一起使用时,它由结构的invokedynamic提供,并由 VM 自动堆叠。NameAndTypeInvokeDynamic
  • \n
\n
\n

重点是我添加的

\n

请注意所有参数通常是如何由 JVM 根据invokedynamic字节码指令自动提供的。在这种情况下,它\xe2\x80\x99是一条消耗一些参数并产生一个指令的指令String,将此引导方法称为知道如何执行操作的实体。

\n

当您想手动调用它时,无论出于何种原因,您\xe2\x80\x99d 必须执行类似的操作

\n
String arg1 = "Hello";\nchar arg2 = \' \';\nString arg3 = "StringConcatFactory";\n\nMethodHandle mh = StringConcatFactory.makeConcat(\n    MethodHandles.lookup(), // normally provided by the JVM\n    "foobar", // normally provided by javac, but meaningless here\n    // method type is normally provided by the JVM and matches the invocation\n    MethodType.methodType(String.class, String.class, char.class, String.class))\n    .getTarget();\n\n// we can now use the handle to perform a concatenation\n// the argument types must match the MethodType specified above\nString result = (String)mh.invokeExact(arg1, arg2, arg3);\n\nSystem.out.println(result);\n
Run Code Online (Sandbox Code Playgroud)\n

您可以重复使用MethodHandle多个字符串连接,但您必须绑定到您在引导期间指定的参数类型\xe2\x80\x99。

\n

对于普通字符串连接表达式,每个表达式在引导过程中都会链接到与固定数量的子表达式及其编译时类型相匹配的句柄。

\n

arg1 + arg2 + arg3\xe2\x80\x99 很难想象直接使用 API 比仅编写等有好处的场景。

\n
\n

makeConcatWithConstants除了可能变化的参数之外,引导方法还允许指定恒定部分。例如,当我们有代码时

\n
String time = switch(LocalTime.now().get(ChronoField.HOUR_OF_DAY) / 6) {\n    case 0 -> "night"; case 1 -> "morning"; case 2 -> "afternoon";\n    case 3 -> "evening"; default -> throw new AssertionError();                \n};\n\nSystem.out.println("Hello "+System.getProperty("user.name")+", good "+time+"!");\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

为了使等效的手动调用更有趣,我们假设系统属性user.name是不变的,因此我们可以在引导调用中将其作为常量提供,使用占位符\\2来引用它,并生成一个仅消耗一个动态参数(时间字符串)的句柄:

\n
MethodHandle mh = StringConcatFactory.makeConcatWithConstants(\n    MethodHandles.lookup(), // normally provided by the JVM\n    "foobar", // normally provided by javac, but meaningless here\n    // method type is normally provided by the JVM and matches the invocation\n    MethodType.methodType(String.class, String.class),\n    "Hello \\2, good \\1!", // recipe, \\1 binds a parameter, \\2 a constant\n    System.getProperty("user.name") // the first constant to bind\n).getTarget();\n\n// we can now use the handle to perform a concatenation\n// the argument types must match the MethodType specified above\nString result = (String)mh.invokeExact(time);\n\nSystem.out.println(result);\n
Run Code Online (Sandbox Code Playgroud)\n

普通的 Java 代码很少会使用附加的constants. 我所知道的唯一场景是具有\\1\\2在原始常量字符串中为了防止它们被解释为占位符,这些子字符串将作为常量提供。

\n

正如此在线代码测试器所示中所演示的,该代码

\n
String time = switch(LocalTime.now().get(ChronoField.HOUR_OF_DAY) / 6) {\n    case 0 -> "night"; case 1 -> "morning"; case 2 -> "afternoon";\n    case 3 -> "evening"; default -> throw new AssertionError();                \n};\n\nSystem.out.println("Hello "+System.getProperty("user.name")+", good "+time+"!");\n\nString tmp = "prefix \\1 "+time+" \\2 suffix";\n
Run Code Online (Sandbox Code Playgroud)\n

被编译为(省略不相关部分):

\n
 0: invokestatic  #1                  // Method java/time/LocalTime.now:()Ljava/time/LocalTime;\n 3: getstatic     #7                  // Field java/time/temporal/ChronoField.HOUR_OF_DAY:Ljava/time/temporal/ChronoField;\n 6: invokevirtual #13                 // Method java/time/LocalTime.get:(Ljava/time/temporal/TemporalField;)I\n 9: bipush        6\n11: idiv\n12: tableswitch   { // 0 to 3\n               0: 44\n               1: 49\n               2: 54\n               3: 59\n         default: 64\n    }\n44: ldc           #17                 // String night\n46: goto          72\n49: ldc           #19                 // String morning\n51: goto          72\n54: ldc           #21                 // String afternoon\n56: goto          72\n59: ldc           #23                 // String evening\n61: goto          72\n64: new           #25                 // class java/lang/AssertionError\n67: dup\n68: invokespecial #27                 // Method java/lang/AssertionError."<init>":()V\n71: athrow\n72: astore_1\n73: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;\n76: ldc           #37                 // String user.name\n78: invokestatic  #39                 // Method java/lang/System.getProperty:(Ljava/lang/String;)Ljava/lang/String;\n81: aload_1\n82: invokedynamic #43,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n87: invokevirtual #47                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V\n90: aload_1\n91: invokedynamic #53,  0             // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;\n96: astore_2\n
Run Code Online (Sandbox Code Playgroud)\n
BootstrapMethods:\n  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;\n    Method arguments:\n      #151 Hello \\u0001, good \\u0001!\n  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;\n    Method arguments:\n      #153 \\u0002\\u0001\\u0002\n      #155 prefix \\u0001\n      #157  \\u0002 suffix\n
Run Code Online (Sandbox Code Playgroud)\n

  • 我扩展了答案 (2认同)