方法“makeconcat”​` 和“stringconcatfactory”中的“makeconcatwithconstants”直接调用api?

cyvaqqii  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(528)

我相信从那以后 Java 9 字符串串联已使用实现 StringConcatFactory .
既然这是作为一个api在java中提供的,那么这些方法如何呢 makeConcat​ 以及 makeConcatWithConstantsStringConcatFactory 直接调用api使用?到目前为止,我还没有找到任何不同使用方法的例子。还有,参数是什么 String name , MethodType concatTypemakeConcat​ 以及 makeConcatWithConstants 和参数 String recipe , Object... constantsmakeConcatWithConstants 意思和应该传递给他们的内容在java文档中不是不言而喻的。

pobjuy32

pobjuy321#

你不应该直接调用这个api。该类被设计为为为 invokedynamic 指令,所以它的api对于该用例是直接的,而不是直接调用。
但文件是详尽的:

参数 lookup -表示具有调用者访问权限的查找上下文。与一起使用时 invokedynamic ,这是由vm自动堆叠的。 name -要实现的方法的名称。此名称是任意的,对于此链接方法没有任何意义。与一起使用时 invokedynamic ,这是由 NameAndTypeInvokeDynamic 结构,并由vm自动堆叠。 concatType -调用站点的预期签名。参数类型表示连接参数的类型;返回类型总是可以从 String . 与一起使用时 invokedynamic ,这是由 NameAndTypeInvokeDynamic 结构,并由vm自动堆叠。

我加的重点
请注意,jvm通常是如何根据 invokedynamic 字节码指令。在这种情况下,它是一条消耗一些参数并产生 String ,将此引导方法称为知道如何执行操作的实体。
无论出于何种原因,当您想手动调用它时,您必须执行以下操作

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 对于多个字符串串联,但您的参数绑定到引导期间指定的参数类型。
对于普通的字符串串联表达式,每个表达式在引导期间都会链接到一个句柄,该句柄与固定数量的子表达式及其编译时类型相匹配。
很难想象这样一种场景:直接使用api比编写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+"!");

我们有几个常量部分,编译器可以使用占位符将它们合并为单个字符串 \1 表示必须插入动态值的位置,因此 recipe 参数将为 "Hello \1, good \1!" . 另一个参数, constants ,将不使用。然后,相应的 invokedynamic 指令只需要提供操作数堆栈上的两个动态值。
为了使等效的手动调用更有趣,我们假设系统属性 user.name 为了保持不变,因此我们可以在引导调用中将其作为常量提供,请使用占位符 \2 要引用它并生成仅使用一个动态参数的句柄,请使用时间字符串:

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 \2, good \1!", // recipe, \1 binds a parameter, \2 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 . 我所知道的唯一一种情况是 \1 或者 \2 在原始常量字符串中。为了防止它们被解释为占位符,这些子字符串将作为常量提供。
正如在这个在线代码测试程序中所演示的,代码

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 \1 "+time+" \2 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
d6kp6zgx

d6kp6zgx2#

您链接的文档中指出,“这些方法通常用作引导方法。。。为了支持字符串连接特性”,也就是说,编译器使用它们来准备进行字符串连接-换句话说,不打算由程序员使用(我记得,它们基本上创建了一个(lambda)方法来调用连接)。
简单的使用方法: + 运算符连接字符串并让编译器执行此操作。如果你真的想直接使用这些方法(为什么?这并不是那么简单),使用连接创建一个类,并使用反编译器检查生成的java代码。
另请参阅:什么是引导方法(不是100%相关,而是最后一句话)

相关问题