代码之家  ›  专栏  ›  技术社区  ›  stefanobaghino

变量函数的编译目的是什么?

  •  3
  • stefanobaghino  · 技术社区  · 6 年前

    在爪哇中,由编译器重新编写变量方法,使它们成为数组的方法,这些数组中的变量是预期的。 this answer )

    斯卡拉怎么了?

    我主要关心的是变量参数是否被隐式复制到 Array 如果传递或不传递另一种类型的集合,即编译器是否会以某种方式重新编写此代码段:

    val strings = Seq("hello")
    "%s".format(strings: _*)
    

    以下内容?

    val strings = Seq("hello")
    "%s".format(strings.toArray: _*)
    

    作为后续问题:差异化方法是否实现Java接口或者纯斯卡拉是否存在差异?

    1 回复  |  直到 6 年前
        1
  •  4
  •   Travis Brown    6 年前

    你可以很容易地通过 javap -v . 如果编译以下代码(使用 String.format 而不是 "%s".format 目前)2.12:

    class Example1 {
      val strings = Seq("foo")
      def formatResult = String.format("%s", strings: _*)
    }
    

    你会得到这个:

      public java.lang.String formatResult();
        descriptor: ()Ljava/lang/String;
        flags: ACC_PUBLIC
        Code:
          stack=4, locals=1, args_size=1
             0: ldc           #21                 // String %s
             2: aload_0
             3: invokevirtual #23                 // Method strings:()Lscala/collection/Seq;
             6: getstatic     #29                 // Field scala/reflect/ClassTag$.MODULE$:Lscala/reflect/ClassTag$;
             9: ldc           #31                 // class java/lang/String
            11: invokevirtual #35                 // Method scala/reflect/ClassTag$.apply:(Ljava/lang/Class;)Lscala/reflect/ClassTag;
            14: invokeinterface #41,  2           // InterfaceMethod scala/collection/Seq.toArray:(Lscala/reflect/ClassTag;)Ljava/lang/Object;
            19: checkcast     #43                 // class "[Ljava/lang/Object;"
            22: invokestatic  #47                 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
            25: areturn
          LineNumberTable:
            line 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      26     0  this   LExample1;
    

    所以是的,它会转换 strings 一个数组。

    如果你使用 “%s”格式 不过,就像这样:

    class Example2 {
      val strings = Seq("foo")
      def formatResult = "%s".format(strings: _*)
    }
    

    您将看不到转换:

      public java.lang.String formatResult();
        descriptor: ()Ljava/lang/String;
        flags: ACC_PUBLIC
        Code:
          stack=4, locals=1, args_size=1
             0: new           #21                 // class scala/collection/immutable/StringOps
             3: dup
             4: getstatic     #27                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
             7: ldc           #29                 // String %s
             9: invokevirtual #33                 // Method scala/Predef$.augmentString:(Ljava/lang/String;)Ljava/lang/String;
            12: invokespecial #37                 // Method scala/collection/immutable/StringOps."<init>":(Ljava/lang/String;)V
            15: aload_0
            16: invokevirtual #39                 // Method strings:()Lscala/collection/Seq;
            19: invokevirtual #43                 // Method scala/collection/immutable/StringOps.format:(Lscala/collection/Seq;)Ljava/lang/String;
            22: areturn
          LineNumberTable:
            line 14: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      23     0  this   LExample2;
    

    这是因为Scala编译器对ValARGS的编码不同于Java编译器(当然对斯卡拉的代码一无所知)。 Seq )您可以强制Scala编译器生成与Java兼容的VARARGS方法。 @varargs 注释:

    class Example3 {
      def foo(xs: String*): Unit = ()
    
      @annotation.varargs
      def bar(xs: String*): Unit = ()
    
      val strings = Seq("foo")
      def fooResult = foo(strings: _*)
      def barResult = bar(strings: _*)
    }
    

    注意这会产生 二者都 但是编码,所以 bar(strings: _*) 仍然不会涉及数组转换,因为scala编译器在这种情况下选择scala编码的方法。

    综上所述:从斯卡拉调用Java VARARGS方法 seq: _* 将始终涉及 toArray 接到电话 SEQ ,但当从scala调用scala varargs方法时不会发生这种情况(无论是否用注释 瓦拉格斯 对于Java兼容性。