日々常々

ふつうのプログラマがあたりまえにしたいこと。

配列引数と可変長引数のクラスファイルでの違い

明日の関ジャバの資料作ってたら気になったので。(全く関係ない)

  • javac 11.0.4

可変長引数のメソッドを持つVaragsクラスとそれを呼び出すInvokerクラスがあってさ。

class Varargs {

  static void methodX(Object... args) {}

  static void methodY(Object[] args) {}
}

class Invoker {

  void methodA() {
    Varargs.methodX();
  }

  void methodB() {
    Varargs.methodX(new Object());
  }
}

Varargs.methodXVarargs.methodY の違いって、可変長で作ってたら引数が空とか配列にしなくても渡せたりとかするわけだけど、外から見えるコンパイル結果に違いがないと、そんな区別ってつかないはずで。メソッド内は配列で扱ってるのは間違いないのだけど。

なので javap してみて。

  static void methodX(java.lang.Object...);
    descriptor: ([Ljava/lang/Object;)V
    flags: (0x0088) ACC_STATIC, ACC_VARARGS
    Code:
      stack=0, locals=1, args_size=1
         0: return

  static void methodY(java.lang.Object[]);
    descriptor: ([Ljava/lang/Object;)V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return

descriptor は同じで、 ACC_VARARGS がついてる。classファイルのフォーマット眺める。

The ACC_VARARGS flag indicates that this method takes a variable number of arguments at the source code level. A method declared to take a variable number of arguments must be compiled with the ACC_VARARGS flag set to 1. All other methods must be compiled with the ACC_VARARGS flag set to 0.

なるほど(ふいんき)。

あとInvokerも javap って眺める。

  void methodA();
    descriptor: ()V
    flags: (0x0000)
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_0
         1: anewarray     #2                  // class java/lang/Object
         4: invokestatic  #3                  // Method Varargs.methodX:([Ljava/lang/Object;)V
         7: return

  void methodB();
    descriptor: ()V
    flags: (0x0000)
    Code:
      stack=5, locals=1, args_size=1
         0: iconst_1
         1: anewarray     #2                  // class java/lang/Object
         4: dup
         5: iconst_0
         6: new           #2                  // class java/lang/Object
         9: dup
        10: invokespecial #1                  // Method java/lang/Object."<init>":()V
        13: aastore
        14: invokestatic  #3                  // Method Varargs.methodX:([Ljava/lang/Object;)V
        17: return

呼び出し側で配列作って渡してる。やっぱそうだよね。

現実逃避終わり。