如何从使用javap的Java学习使用scala.None?

如何从使用javap的Java学习使用scala.None?,scala,scala-java-interop,javap,Scala,Scala Java Interop,Javap,在前面的一个问题中,人们似乎使用了javap来了解如何从Java访问scala.None。我想知道他们是怎么做到的。仅供参考,答案是: scala.Option$.MODULE$.apply(null); 可短路至: scala.Option.apply(null); 给定此程序(OptionTest.scala): 我在上面运行了javap,如下所示: javap -s -c -l -private OptionTest 这是javap输出的一部分: public static fina

在前面的一个问题中,人们似乎使用了
javap
来了解如何从Java访问
scala.None
。我想知道他们是怎么做到的。仅供参考,答案是:

scala.Option$.MODULE$.apply(null);
可短路至:

scala.Option.apply(null);
给定此程序(
OptionTest.scala
):

我在上面运行了
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
我还在
scala.None
scala.Option
上运行了javap。如何从
javap
输出中得出:

  • None
    是扩展
    选项的
    None.type
    type的唯一对象
  • 需要用于伴生对象的
    apply()
    方法

  • Scala代码如何编译成JVM字节码有一些规则。由于潜在的名称冲突,生成的代码并不总是直观易懂的,但如果规则已知,则可以在Java中访问编译的Scala代码

    注意:在写这篇文章时,我注意到javac和eclipsejavac在从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);
    
    请注意,对于没有参数列表的方法,将创建一个空参数列表。多个参数列表合并为一个参数列表

    字段、值 对于类
    类X(vari:Int)
    将创建getter和setter。对于类
    类X(val i:Int)
    只创建一个Getter:

    //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为每个特殊符号生成一个特定的名称。有一个类可以对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)
    
    案例类 Scala编译器会自动为case类生成apply方法,并为字段生成getter。案例类
    案例类X(i:Int)
    易于访问:

    new X(3).i();
    X$.MODULE$.apply(3);
    
    特点 trait
    trait T{def m}
    只包含抽象成员,它被编译成一个接口,该接口位于名为
    T.class
    的类文件中。因此,它可以通过Java类轻松实现:

    class X implements T {
      public void m() {
        // do stuff here
      }
    }
    
    如果trait包含具体的成员,那么除了普通接口之外,还会生成一个名为
    $class.class
    的类文件。特质

    trait T {
      def m1
      def m2 = 5
    }
    
    也可以在Java中轻松实现。class文件
    T$class.class
    包含trait的具体成员,但它们似乎无法从Java访问。javac和eclipsejavac都不会编译对此类的访问

    可以找到更多关于特征如何编译的细节

    功能 函数文本被编译为FunctionN类的匿名实例。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
      }
    }
    
    编译为普通类文件,如上所述。此外,每个函数文本都有自己的类文件。因此,对于函数值,将生成名为
    $$anonfun$.class
    的类文件,其中N是一个连续数。对于函数方法(返回函数的方法),将生成名为
    $$anonfun$$.class
    的类文件。函数名的各个部分由美元符号分隔,在
    anonfun
    标识符前面还有两个美元符号。对于嵌套函数,嵌套函数的名称将附加到外部函数,这意味着内部函数将获得一个类文件,如
    $$anonfun$$$$$$anonfun$$.class
    。当内部函数没有名称时,如
    h
    中所示,它将获得名称
    apply

    这意味着在我们的案例中,我们得到:

    • X$$anonfun$1.class
      用于f
    • X$$anonfun$g$1.类
      g
    • X$$anonfun$h$1$$anonfun$apply$1.class
      h
    • X$$anonfun$i$1.class
      X$$anonfun$i$1$$anonfun$j$1.class
      用于i和j
    要访问它们,请使用其应用方法:

    X.f().apply(7);
    X.g().apply(7);
    X.h().apply(3).apply(5);
    X.i().apply(3).apply(5);
    
    回答问题 你应该知道:

    • 普通Scala类可以通过其构造函数或apply方法访问
    • 当没有构造函数时,就会有一个apply方法
    • 当没有构造函数和apply方法时,就会有另一个类文件,其名称与调用该类的方式相同,并在末尾附加一个美元符号。在此类中搜索
      模块$
      字段
    • 构造函数和apply方法是继承的,因此如果在子类中找不到任何内容,请搜索超类
    一些例子 选项 javap说它有一个构造函数和一个apply方法。此外,它还说这个类是抽象的。因此,只能使用apply方法:

    Option.apply(3);
    
    一些 它有一个构造函数和一个apply方法(因为我们知道Option有一个和一些extends选项)。使用其中的一个,并感到快乐:

    new Some<Integer>(3);
    Some.apply(3);
    
    它没有构造函数,没有apply方法,也没有扩展选项。因此,我们将查看
    None$

    // javap -private scala.None$
    public final class scala.None$ extends scala.Option implements ... {
      ...
      public static final scala.None$ MODULE$;
      private scala.None$();
    }
    
    是啊!我们找到了一个
    模块$
    字段和选项的应用方法。此外,我们还发现了私有构造函数:

    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$
    。它有一个apply方法,该方法需要一个
    scala.collection.Seq
    。所以要得到一个列表,我们首先需要一个Seq。但如果我们看一下Seq,就没有apply方法了。此外,当我们查看Seq的超类和scala.collection.Seq$
    时,我们只能找到一个需要Seq的apply方法。那么,该怎么办

    我们必须看看scalac如何创建List或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
    
    }
    
    这看起来很难看,但它起作用了
    // javap scala.None
    public final class scala.None extends java.lang.Object{
      ...
    }
    
    // javap -private scala.None$
    public final class scala.None$ extends scala.Option implements ... {
      ...
      public static final scala.None$ MODULE$;
      private scala.None$();
    }
    
    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
    
    class X {
      val xs = List(1, 2, 3)
    }
    
    // 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
    
    }
    
    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> :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