質問

前の質問で、 JavaからScala.Noneにアクセスします, 、人々が使用していたようです javap アクセス方法を把握するため scala.None Javaから。彼らがそれをどのようにしたか知りたいです。参考までに、答えは次のとおりです。

scala.Option$.MODULE$.apply(null);

短くすることができます:

scala.Option.apply(null);

このプログラムを与えられた(OptionTest.scala):

object OptionTest extends App {
  val x = scala.None
  val y = scala.Some("asdf")
}

走った javap このように:

javap -s -c -l -private OptionTest

これはの一部です javap 出力:

public static final scala.None$ x();
  Signature: ()Lscala/None$;
  Code:
   0:   getstatic  #11; //Field OptionTest$.MODULE$:LOptionTest$;
   3:   invokevirtual  #55; //Method OptionTest$.x:()Lscala/None$;
   6:   areturn

私もjavapを走らせました scala.Nonescala.Option. 。からどのように把握できますか javap 出力:

  1. None の唯一のオブジェクトです None.type 拡張するタイプ Option
  2. apply() コンパニオンオブジェクトの方法が必要です

?

役に立ちましたか?

解決

SCALAコードがJVM-ByteCodeにコンパイルされる方法はルールがあります。潜在的な名前の衝突のため、生成されたコードは常に直感的ではありませんが、ルールが既知の場合は、Java内のコンパイルされたScalaコードにアクセスすることが可能です。

注意: これを書いている間、私はJavacとEclipse-JavacがJavaのScalaコードにアクセスする際に異なる行動をとることに気付きました。以下のコードは、そのうちのコードと他のコードではコンパイルされない可能性があります。

クラス、コンストラクター、メソッド

ここには特別なルールはありません。次のScalaクラス

class X(i: Int) {
  def m1 = i*2
  def m2(a: Int)(b: Int) = a*b
  def m3(a: Int)(implicit b: Int) = a*b
}

通常のJavaクラスのようにアクセスできます。名前付きのファイルにコンパイルされます X.class:

X x = new X(7);
x.m1();
x.m2(3, 5);
x.m3(3, 5);

ParameterListのないメソッドの場合、空のparameterListが作成されていることに注意してください。複数のパラメーターリストが単一のパラメーターリストにマージされます。

フィールド、値

クラスの場合 class X(var i: Int) ゲッターとセッターが作成されます。クラスの場合 class X(val i: Int) ゲッターのみが作成されます:

//Scala
val x = new X(5)
x.i = 3 // Setter
x.i // Getter

//Java
X x = new X(5);
x.i_$eq(3); // Setter
x.i(); // Getter

Javaでは、識別子が特別な標識を含めることは許可されていないことに注意してください。したがって、Scalacはこれらの特別な標識のそれぞれに対して特定の名前を生成します。クラスがあります scala.reflect.nameTransformer OPSをエンコード/デコードできます。

scala> import scala.reflect.NameTransformer._
import scala.reflect.NameTransformer._

scala> val ops = "~=<>!#%^&|*/+-:\\?@"
ops: String = ~=<>!#%^&|*/+-:\?@

scala> ops map { o => o -> encode(o.toString) } foreach println
(~,$tilde)
(=,$eq)
(<,$less)
(>,$greater)
(!,$bang)
(#,$hash)
(%,$percent)
(^,$up)
(&,$amp)
(|,$bar)
(*,$times)
(/,$div)
(+,$plus)
(-,$minus)
(:,$colon)
(\,$bslash)
(?,$qmark)
(@,$at)

クラス class X { var i = 5 } コンストラクターでフィールドが作成されたときと同じスキーマによって翻訳されます。変数への直接アクセス i Javaからはプライベートであるため、不可能です。

オブジェクト

JavaにはScalaオブジェクトのようなものはありません。したがって、Scalacは魔法をかけなければなりません。オブジェクトの場合 object X { val i = 5 } 2つのJVMクラスファイルが生成されます。 X.classX$.class. 。最初のものはインターフェイスのように機能し、SCALAオブジェクトのフィールドとメソッドにアクセスする静的メソッドが含まれています。後者は、インスタンス化できないシングルトンクラスです。クラスのシングルトンインスタンスを保持するフィールドがあります。 MODULE$, 、シングルトンへのアクセスを可能にします:

X.i();
X$.MODULE$.i();

ケースクラス

SCALAコンパイラは、ケースクラスのApply-Methodを自動的に生成し、フィールドのゲッターを生成します。ケースクラス case class X(i: Int) 簡単にアクセスできます:

new X(3).i();
X$.MODULE$.apply(3);

特性

特性 trait T { def m }, 、抽象メンバーのみを含む、インターフェイスにコンパイルされ、名前のクラスファイルに配置されます T.class. 。したがって、Javaクラスによって簡単に実装できます。

class X implements T {
  public void m() {
    // do stuff here
  }
}

特性に具体的なメンバーが含まれている場合、名前のクラスファイルがあります <trait_name>$class.class 通常のインターフェイスに加えて生成されます。特性

trait T {
  def m1
  def m2 = 5
}

Java内で簡単に実装することもできます。クラスファイル T$class.class 特性の具体的なメンバーが含まれていますが、Javaからアクセスすることは不可能であるようです。 JavacもEclipse-Javacも、このクラスへのアクセスをコンパイルしません。

特性がどのように編集されるかについてのいくつかの詳細を見つけることができます ここ.

機能

関数リテラルは、クラス関数の匿名インスタンスとしてコンパイルされます。 SCALAオブジェクト

object X {
  val f: Int => Int = i => i*2
  def g: Int => Int = i => i*2
  def h: Int => Int => Int = a => b => a*b
  def i: Int => Int => Int = a => {
    def j: Int => Int = b => a*b
    j
  }
}

上記のように、通常のクラスファイルにコンパイルされています。さらに、各関数リテラルは独自のクラスファイルを取得します。したがって、関数の場合、名前のクラスファイルが値です <class_name>$$anonfun$<N>.class 生成されます。ここで、nは連続数です。関数メソッド(メソッド、関数を返す方法)の名前のクラスファイル <class_name>$$anonfun$<method_name>$<N>.class 生成されます。関数名の部分はドル記号で区切られ、 anonfun 識別子も2つのドルサインがあります。ネストされた関数の場合、ネストされた関数の名前は外部関数に追加されます。これは、内部関数が次のようなクラスファイルを取得することを意味します <class_name>$$anonfun$<outer_method_name>$<N>$$anonfun$<inner_method_name>$<N>.class. 。内側の関数に名前がない場合、 h 名前を取得します apply.

これは、私たちの場合、次のことを意味します。

  • X$$anonfun$1.class fの場合
  • X$$anonfun$g$1.class gの場合
  • X$$anonfun$h$1$$anonfun$apply$1.class hの場合
  • X$$anonfun$i$1.classX$$anonfun$i$1$$anonfun$j$1$1.class 私とjのため

アクセスするには、apply-methodを使用します。

X.f().apply(7);
X.g().apply(7);
X.h().apply(3).apply(5);
X.i().apply(3).apply(5);

質問に答えて

あなたが知っておくべき:

  • 通常のSCALAクラスは、コンストラクターまたはアプリケーションメソッドによってアクセスできます
  • コンストラクターがない場合は、Apply-Methodがあります
  • コンストラクターがなく、適用方法がない場合、クラスが呼び出されるのと同じように名前が付けられた別のクラスファイルがある場合、最後にドル記号を追加します。このクラスを検索してください MODULE$ 分野
  • コンストラクターと応用メソッドが継承されているので、サブクラスに何も見つからない場合はスーパークラスを検索します

いくつかの例

オプション

// javap scala.Option
public abstract class scala.Option extends java.lang.Object implements ... {
  ...
  public static final scala.Option apply(java.lang.Object);
  public scala.Option();
}

Javapは、コンストラクターと適用方法があると言います。さらに、クラスは抽象的であると書かれています。したがって、Apply-Methodのみを使用できます。

Option.apply(3);

いくつか

// javap scala.Some
public final class scala.Some extends scala.Option implements ... {
  ...
  public scala.Some(java.lang.Object);
}

コンストラクターとApply-Methodがあります(オプションに1つと拡張オプションがあることがわかっているため)。それらの1つを使用して幸せになります:

new Some<Integer>(3);
Some.apply(3);

なし

// javap scala.None
public final class scala.None extends java.lang.Object{
  ...
}

コンストラクターも適用されておらず、オプションを拡張していません。だから、私たちは見ていきます None$:

// javap -private scala.None$
public final class scala.None$ extends scala.Option implements ... {
  ...
  public static final scala.None$ MODULE$;
  private scala.None$();
}

うん! aを見つけました MODULE$ フィールドとオプションの適用測定。さらに、プライベートコンストラクターが見つかりました。

None$.apply(3) // returns Some(3). Please use the apply-method of Option instead
None$.MODULE$.isDefined(); // returns false
new None$(); // compiler error. constructor not visible

リスト

scala.collection.immutable.List 抽象的なので、使用する必要があります scala.collection.immutable.List$. 。 Anを期待するApply-Methodがあります scala.collection.Seq. 。したがって、リストを取得するには、最初にseqが必要です。しかし、seqを見ると、適用測定はありません。さらに、Seqのスーパークラスとで見ると scala.collection.Seq$ seqを期待する適用測定のみを見つけることができます。じゃあ何をすればいいの?

Scalacがリストまたはseqのインスタンスをどのように作成するかを見てみる必要があります。最初にSCALAクラスを作成します:

class X {
  val xs = List(1, 2, 3)
}

Scalacでコンパイルし、Javapでクラスファイルを見てください。

// javap -c -private X
public class X extends java.lang.Object implements scala.ScalaObject{
...
public X();
  Code:
   0:   aload_0
   1:   invokespecial   #20; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   getstatic   #26; //Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$;
   8:   getstatic   #31; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   11:  iconst_3
   12:  newarray int
   14:  dup
   15:  iconst_0
   16:  iconst_1
   17:  iastore
   18:  dup
   19:  iconst_1
   20:  iconst_2
   21:  iastore
   22:  dup
   23:  iconst_2
   24:  iconst_3
   25:  iastore
   26:  invokevirtual   #35; //Method scala/Predef$.wrapIntArray:([I)Lscala/collection/mutable/WrappedArray;
   29:  invokevirtual   #39; //Method scala/collection/immutable/List$.apply:(Lscala/collection/Seq;)Lscala/collection/immutable/List;
   32:  putfield    #13; //Field xs:Lscala/collection/immutable/List;
   35:  return

}

コンストラクターは興味深いです。 1、2、および3で満たされたINTの配列(l。12)が作成されていることがわかります(l。14-25)。その後、この配列はに配信されます scala.Predef$.wrapIntArray (l。26)。この結果 scala.collection.mutable.WrappedArray 再びリストに配信されます(l。29)。最後に、リストはフィールドに保存されます(l。32)。 Javaでリストを作成したいときは、同じことをしなければなりません。

int[] arr = { 1, 2, 3 };
WrappedArray<Object> warr = Predef$.MODULE$.wrapIntArray(arr);
List$.MODULE$.apply(warr);

// or shorter
List$.MODULE$.apply(Predef$.MODULE$.wrapIntArray(new int[] { 1, 2, 3 }));

これは醜く見えますが、うまくいきます。 Scalaライブラリへのアクセスを包む見栄えの良いライブラリを作成すると、JavaのScalaを使いやすくなります。

概要

ScalaコードがBytecodeにコンパイルされる方法がいくつかあることを知っています。しかし、上記の情報では、これらのルールを自分で見つけることができるはずだと思います。

他のヒント

私は他の答えと競合していませんが、人々はしばしばこれに気付かないように思われるので、あなたはこれをREPLで行うことができます。

scala> :paste
// Entering paste mode (ctrl-D to finish)

object OptionTest extends App {
  val x = scala.None
  val y = scala.Some("asdf")
}

// Exiting paste mode, now interpreting.

defined module OptionTest

scala> :javap -v OptionTest$
Compiled from "<console>"
public final class OptionTest$ extends java.lang.Object implements scala.App,scala.ScalaObject
  SourceFile: "<console>"
  Scala: length = 0x

  [lots of output etc]   

  public scala.None$ x();
    Code:
     Stack=1, Locals=1, Args_size=1
     0: aload_0
     1: getfield    #65; //Field x:Lscala/None$;
     4: areturn
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top