Для чего скомпилированы функции с переменными числами? - PullRequest
0 голосов
/ 23 января 2019

В Java переменные методы переписываются компилятором так, что они становятся методами, которые принимают массив, в котором ожидаются переменные аргументы (согласно этому ответу ).

Чтопроисходит в Scala?

Моя главная проблема заключается в том, копируются ли неявно аргументы variadic в Array, если передан другой тип коллекции или нет, т. е. будет ли компилятор каким-то образом переписывать этот фрагмент:

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

к следующему?

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

В качестве дополнительного вопроса: есть ли различия в том, реализует ли метод с переменным числом элементов интерфейс Java или это чистый Scala?

1 Ответ

0 голосов
/ 23 января 2019

Вы можете легко проверить это с помощью 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".format, но вот так:

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кодировка varargs отличается от кодировки Java-компилятора (который, конечно, ничего не знает о Seq в Scala).Вы можете заставить компилятор 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 из Scala с seq: _* всегда будет включать toArray вызовна Seq, хотя этого не произойдет при вызове методов Scala varargs из Scala (независимо от того, помечены они или нет @varargs для совместимости с Java).

...