如何学习如何使用scala。没有使用javap的Java ?

时间:2021-10-14 19:37:25

In a previous question, Accessing scala.None from Java, it seems that people had used javap to figure out how to access scala.None from Java. I would like to know how they did that. FYI, the answer is:

在前面的问题中,访问scala。从Java中没有,似乎人们已经使用javap找到了如何访问scala的方法。从Java。我想知道他们是怎么做到的。通知你,答案是:

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

which can be shorted to:

可以缩短为:

scala.Option.apply(null);

Given this program (OptionTest.scala):

鉴于这个项目(OptionTest.scala):

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

I ran javap on it like this:

我对它运行javap是这样的:

javap -s -c -l -private OptionTest

This is a portion of the javap output:

这是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

I also ran javap on scala.None and scala.Option. How would one figure out from the javap output that:

我还在scala上运行了javap。没有和scala.Option。如何从javap输出中得出:

  1. None is a sole object of None.type type which extends Option
  2. 没有一个是唯一的目标。扩展选项的类型类型。
  3. The apply() method for the companion object is required
  4. 需要为伴生对象使用apply()方法

?

吗?

2 个解决方案

#1


71  

There are rules how Scala code is compiled to JVM-bytecode. Because of potential name *es the generated code is not always intuitive to understand but if the rules are known it is possible to get access to the compiled Scala code within Java.

Scala代码是如何编译成jvm -字节码的。由于潜在的名称冲突,生成的代码并不总是很容易理解,但是如果规则是已知的,那么就有可能在Java中访问编译过的Scala代码。

Attention: While writing this, I noticed that javac and eclipse-javac behave differently in accessing Scala code from Java. It is possible that the code below compile with one of them but not with the other.

注意:在编写本文时,我注意到在从Java访问Scala代码时,javac和eclipse-javac的行为是不同的。下面的代码可能是用其中之一编译的,而不是用另一个。

Classes, Constructors, Methods

There are no special rules here. The following Scala class

这里没有特别的规定。以下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
}

can be accessed like a normal Java class. It is compiled to a file named X.class:

可以像普通Java类一样访问。它被编译成一个名为X.class的文件:

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

Notice, that for methods without a parameterlist an empty parameterlist is created. Multiple parameterlists are merged to a single one.

注意,对于没有参数列表的方法,会创建一个空的参数列表。多个参数列表合并为一个参数列表。

Fields, Values

For a class class X(var i: Int) Getters and Setters are created. For a class class X(val i: Int) only a Getter is created:

对于类X(var i: 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

Notice, that in Java an identifier is not allowed to include special signs. Therefore scalac generates for each of these special signs a specific name. There is a class scala.reflect.NameTransformer which can encode/decode the ops:

注意,在Java中,标识符不允许包含特殊符号。因此,scalac为这些特殊符号生成一个特定的名称。有一个班级,反映。能对ops进行编码/解码的NameTransformer:

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)

A class class X { var i = 5 } is translated by the same schema as when the field is created in the constructor. Direct access to the variable i from Java is not possible, because it is private.

类X {var i = 5}被转换为与在构造函数中创建字段时相同的模式。从Java直接访问变量i是不可能的,因为它是私有的。

Objects

There is no such thing as a Scala object in Java. Therefore scalac has to do some magic. For an object object X { val i = 5 } two JVM-class files are generated: X.class and X$.class. The first one works like an interface, it includes static methods to access fields and methods of the Scala object. The latter is a singleton class which cannot be instantiated. It has a Field which holds the singleton instance of the class, named MODULE$, which allows access to the singleton:

在Java中没有Scala对象这样的东西。因此scalac必须做一些魔术。对于对象,生成两个jvm类文件:X。类和X $ . class。第一个工作就像一个接口,它包含了访问Scala对象的字段和方法的静态方法。后者是一个单例类,不能实例化。它有一个字段,其中包含类的单例实例,名为MODULE$,允许访问单例:

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

Case classes

The Scala compiler automatically generates an apply-method for a case class and Getters for fields. The case class case class X(i: Int) is easily accessed:

Scala编译器自动为case类生成一个apply方法,为字段生成getter。case类case类case X(i: Int)很容易访问:

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

Traits

A trait trait T { def m }, which contains only abstract members, is compiled to an interface, which is placed in a class files named T.class. Therefore it can easily implemented by a Java class:

只包含抽象成员的特征属性T {def m}被编译为一个接口,该接口被放在名为T.class的类文件中。因此,Java类可以很容易地实现它:

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

If the trait contains concrete members there is a class file named <trait_name>$class.class generated, additionally to the normal interface. The trait

如果特征包含具体的成员,则有一个名为 $class的类文件。类生成,附加到普通接口。的特征

trait T {
  def m1
  def m2 = 5
}

can also easily implemented within Java. The class file T$class.class contains the concrete members of the trait, but it seems that they are impossible to access from Java. Neither javac nor the eclipse-javac will compile an access to this class.

也可以在Java中轻松实现。台币类的类文件。类包含特性的具体成员,但是似乎不可能从Java访问它们。无论是javac还是eclipse-javac都不会编译对该类的访问。

Some more detail about how traits are compiled can be found here.

关于如何编译性状的更多细节可以在这里找到。

Functions

Function literals are compiled as anonymous instances of the classes FunctionN. A Scala object

函数文本被编译为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
  }
}

is compiled to the normal class-files, as describes above. Furthermore each function literal gets its own class-file. So, for function values a class file named <class_name>$$anonfun$<N>.class is generated, where N is a continuous number. For function methods (methods, which return a function) a class file named <class_name>$$anonfun$<method_name>$<N>.class is generated. The parts of the function name are separated by dollar signs and in front of the anonfun identifier there are also two dollar signs. For nested functions the name of the nested function is appended to the outer function, this means an inner function will get a class file like <class_name>$$anonfun$<outer_method_name>$<N>$$anonfun$<inner_method_name>$<N>.class. When an inner function does not have a name, as seen in h it gets the name apply.

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

This means in our case we get:

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

  • X$$anonfun$1.class for f
  • X $ $ anonfun 1美元。f类
  • X$$anonfun$g$1.class for g
  • X $ $ anonfun g 1美元。类克
  • X$$anonfun$h$1$$anonfun$apply$1.class for h
  • X anonfun h 1美元美元美元anonfun申请美元1美元。类h
  • X$$anonfun$i$1.class and X$$anonfun$i$1$$anonfun$j$1$1.class for i and j
  • X $ $ anonfun我1美元。类和X anonfun我$ 1 $美元anonfun美元$ j $ 1 $ 1。i和j的类

To access them use their apply-method:

使用他们的应用方法访问它们:

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

Answer the question

You should know:

你应该知道:

  • a normal Scala class can accessed by their constructors or their apply-methods
  • 普通的Scala类可以由它们的构造函数或它们的应用程序访问。
  • when there is no constructor than there is an apply-method
  • 当没有构造函数时,就会有一个apply-method
  • when there is no constructor and no apply method than there is a another class file named the same way the class is called which appends a dollar sign at the end. Search this class for a MODULE$ field
  • 当没有构造函数和apply方法时,就会出现另一个类文件,其名称与调用类的方式相同,并在末尾添加一个$符号。在这个类中搜索模块$字段
  • constructors and apply-methods are inherited, so search the super-classes if you can't find anything in the subclasses
  • 构造函数和应用程序方法是继承的,所以如果在子类中找不到任何东西,可以搜索超类

Some examples

Option

// 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 says it has a constructor and an apply method. Furthermore it says the class is abstract. Thus only the apply-method can used:

javap说它有一个构造函数和一个apply方法。而且它说这个类是抽象的。因此,只有apply-method才能使用:

Option.apply(3);

Some

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

It has a constructor and an apply-method (because we know Option has one and Some extends Option). Use one of them and be happy:

它有一个构造函数和一个apply-method(因为我们知道选项有一个和一些extend选项)。使用其中一个,然后快乐:

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

None

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

It has no constructor, no apply-method and doesn't extend Option. So, we will take a look to None$:

它没有构造函数,没有应用程序方法,也没有扩展选项。所以,我们来看看no $:

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

Yeah! We found a MODULE$ field and the apply-method of Option. Furthermore we found the private constructor:

是啊!我们找到了一个模块$字段和选项的应用方法。此外,我们还发现了私有构造函数:

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

List

scala.collection.immutable.List is abstract, thus we have to use scala.collection.immutable.List$. It has an apply-method which expects an scala.collection.Seq. So to get a List we need first a Seq. But if we look to Seq there is no apply-method. Furthermore when we look at the super-classes of Seq and at scala.collection.Seq$ we can only find an apply-methods which expects a Seq. So, what to do?

scala.collection.immutable。列表是抽象的,因此我们必须使用scala.collection.immutable.List$。它有一个应用程序方法,期望得到一个scala.collection.Seq。为了得到一个列表,我们首先需要一个Seq。但是如果我们看一下Seq,就会发现没有应用方法。此外,当我们看Seq和scala.collection的超类时。我们只能找到需要Seq的应用方法。那么,怎么做?

We have to take a look how scalac creates an instance of List or Seq. First create a Scala class:

我们必须看看scalac是如何创建List或Seq的实例的。首先创建一个Scala类:

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

Compile it with scalac and look at the class file with javap:

用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

}

The constructor is interesting. It tells us, that an array of ints is created (l. 12) which is filled with 1, 2 and 3. (l. 14-25). After that this array is delivered to scala.Predef$.wrapIntArray (l. 26). This resulting scala.collection.mutable.WrappedArray is again delivered to our List (l. 29). At the end, the List is stored in the field (l. 32). When we wanna create a List in Java, we have to do the same:

构造函数是有趣的。它告诉我们,创建了一个ints数组(l. 12),其中填充了1、2和3。(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 }));

This looks ugly, but it works. If you create a nice looking library which wraps the access to the Scala library it will be easy to use Scala from Java.

这看起来很丑,但确实有效。如果您创建了一个外观漂亮的库来封装对Scala库的访问,那么从Java使用Scala就很容易了。

Summary

I know there are some more rules how Scala code is compiled to bytecode. But I think with the information above it should be possible to find these rules by yourself.

我知道Scala代码如何编译成字节码还有一些规则。但是我认为有了上面的信息,你就可以自己找到这些规则了。

#2


15  

I'm not competing with the other answer, but since people seem often not to notice this, you can do this in the repl.

我并不是在与另一个答案竞争,但是由于人们似乎常常没有注意到这一点,你可以在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

#1


71  

There are rules how Scala code is compiled to JVM-bytecode. Because of potential name *es the generated code is not always intuitive to understand but if the rules are known it is possible to get access to the compiled Scala code within Java.

Scala代码是如何编译成jvm -字节码的。由于潜在的名称冲突,生成的代码并不总是很容易理解,但是如果规则是已知的,那么就有可能在Java中访问编译过的Scala代码。

Attention: While writing this, I noticed that javac and eclipse-javac behave differently in accessing Scala code from Java. It is possible that the code below compile with one of them but not with the other.

注意:在编写本文时,我注意到在从Java访问Scala代码时,javac和eclipse-javac的行为是不同的。下面的代码可能是用其中之一编译的,而不是用另一个。

Classes, Constructors, Methods

There are no special rules here. The following Scala class

这里没有特别的规定。以下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
}

can be accessed like a normal Java class. It is compiled to a file named X.class:

可以像普通Java类一样访问。它被编译成一个名为X.class的文件:

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

Notice, that for methods without a parameterlist an empty parameterlist is created. Multiple parameterlists are merged to a single one.

注意,对于没有参数列表的方法,会创建一个空的参数列表。多个参数列表合并为一个参数列表。

Fields, Values

For a class class X(var i: Int) Getters and Setters are created. For a class class X(val i: Int) only a Getter is created:

对于类X(var i: 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

Notice, that in Java an identifier is not allowed to include special signs. Therefore scalac generates for each of these special signs a specific name. There is a class scala.reflect.NameTransformer which can encode/decode the ops:

注意,在Java中,标识符不允许包含特殊符号。因此,scalac为这些特殊符号生成一个特定的名称。有一个班级,反映。能对ops进行编码/解码的NameTransformer:

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)

A class class X { var i = 5 } is translated by the same schema as when the field is created in the constructor. Direct access to the variable i from Java is not possible, because it is private.

类X {var i = 5}被转换为与在构造函数中创建字段时相同的模式。从Java直接访问变量i是不可能的,因为它是私有的。

Objects

There is no such thing as a Scala object in Java. Therefore scalac has to do some magic. For an object object X { val i = 5 } two JVM-class files are generated: X.class and X$.class. The first one works like an interface, it includes static methods to access fields and methods of the Scala object. The latter is a singleton class which cannot be instantiated. It has a Field which holds the singleton instance of the class, named MODULE$, which allows access to the singleton:

在Java中没有Scala对象这样的东西。因此scalac必须做一些魔术。对于对象,生成两个jvm类文件:X。类和X $ . class。第一个工作就像一个接口,它包含了访问Scala对象的字段和方法的静态方法。后者是一个单例类,不能实例化。它有一个字段,其中包含类的单例实例,名为MODULE$,允许访问单例:

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

Case classes

The Scala compiler automatically generates an apply-method for a case class and Getters for fields. The case class case class X(i: Int) is easily accessed:

Scala编译器自动为case类生成一个apply方法,为字段生成getter。case类case类case X(i: Int)很容易访问:

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

Traits

A trait trait T { def m }, which contains only abstract members, is compiled to an interface, which is placed in a class files named T.class. Therefore it can easily implemented by a Java class:

只包含抽象成员的特征属性T {def m}被编译为一个接口,该接口被放在名为T.class的类文件中。因此,Java类可以很容易地实现它:

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

If the trait contains concrete members there is a class file named <trait_name>$class.class generated, additionally to the normal interface. The trait

如果特征包含具体的成员,则有一个名为 $class的类文件。类生成,附加到普通接口。的特征

trait T {
  def m1
  def m2 = 5
}

can also easily implemented within Java. The class file T$class.class contains the concrete members of the trait, but it seems that they are impossible to access from Java. Neither javac nor the eclipse-javac will compile an access to this class.

也可以在Java中轻松实现。台币类的类文件。类包含特性的具体成员,但是似乎不可能从Java访问它们。无论是javac还是eclipse-javac都不会编译对该类的访问。

Some more detail about how traits are compiled can be found here.

关于如何编译性状的更多细节可以在这里找到。

Functions

Function literals are compiled as anonymous instances of the classes FunctionN. A Scala object

函数文本被编译为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
  }
}

is compiled to the normal class-files, as describes above. Furthermore each function literal gets its own class-file. So, for function values a class file named <class_name>$$anonfun$<N>.class is generated, where N is a continuous number. For function methods (methods, which return a function) a class file named <class_name>$$anonfun$<method_name>$<N>.class is generated. The parts of the function name are separated by dollar signs and in front of the anonfun identifier there are also two dollar signs. For nested functions the name of the nested function is appended to the outer function, this means an inner function will get a class file like <class_name>$$anonfun$<outer_method_name>$<N>$$anonfun$<inner_method_name>$<N>.class. When an inner function does not have a name, as seen in h it gets the name apply.

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

This means in our case we get:

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

  • X$$anonfun$1.class for f
  • X $ $ anonfun 1美元。f类
  • X$$anonfun$g$1.class for g
  • X $ $ anonfun g 1美元。类克
  • X$$anonfun$h$1$$anonfun$apply$1.class for h
  • X anonfun h 1美元美元美元anonfun申请美元1美元。类h
  • X$$anonfun$i$1.class and X$$anonfun$i$1$$anonfun$j$1$1.class for i and j
  • X $ $ anonfun我1美元。类和X anonfun我$ 1 $美元anonfun美元$ j $ 1 $ 1。i和j的类

To access them use their apply-method:

使用他们的应用方法访问它们:

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

Answer the question

You should know:

你应该知道:

  • a normal Scala class can accessed by their constructors or their apply-methods
  • 普通的Scala类可以由它们的构造函数或它们的应用程序访问。
  • when there is no constructor than there is an apply-method
  • 当没有构造函数时,就会有一个apply-method
  • when there is no constructor and no apply method than there is a another class file named the same way the class is called which appends a dollar sign at the end. Search this class for a MODULE$ field
  • 当没有构造函数和apply方法时,就会出现另一个类文件,其名称与调用类的方式相同,并在末尾添加一个$符号。在这个类中搜索模块$字段
  • constructors and apply-methods are inherited, so search the super-classes if you can't find anything in the subclasses
  • 构造函数和应用程序方法是继承的,所以如果在子类中找不到任何东西,可以搜索超类

Some examples

Option

// 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 says it has a constructor and an apply method. Furthermore it says the class is abstract. Thus only the apply-method can used:

javap说它有一个构造函数和一个apply方法。而且它说这个类是抽象的。因此,只有apply-method才能使用:

Option.apply(3);

Some

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

It has a constructor and an apply-method (because we know Option has one and Some extends Option). Use one of them and be happy:

它有一个构造函数和一个apply-method(因为我们知道选项有一个和一些extend选项)。使用其中一个,然后快乐:

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

None

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

It has no constructor, no apply-method and doesn't extend Option. So, we will take a look to None$:

它没有构造函数,没有应用程序方法,也没有扩展选项。所以,我们来看看no $:

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

Yeah! We found a MODULE$ field and the apply-method of Option. Furthermore we found the private constructor:

是啊!我们找到了一个模块$字段和选项的应用方法。此外,我们还发现了私有构造函数:

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

List

scala.collection.immutable.List is abstract, thus we have to use scala.collection.immutable.List$. It has an apply-method which expects an scala.collection.Seq. So to get a List we need first a Seq. But if we look to Seq there is no apply-method. Furthermore when we look at the super-classes of Seq and at scala.collection.Seq$ we can only find an apply-methods which expects a Seq. So, what to do?

scala.collection.immutable。列表是抽象的,因此我们必须使用scala.collection.immutable.List$。它有一个应用程序方法,期望得到一个scala.collection.Seq。为了得到一个列表,我们首先需要一个Seq。但是如果我们看一下Seq,就会发现没有应用方法。此外,当我们看Seq和scala.collection的超类时。我们只能找到需要Seq的应用方法。那么,怎么做?

We have to take a look how scalac creates an instance of List or Seq. First create a Scala class:

我们必须看看scalac是如何创建List或Seq的实例的。首先创建一个Scala类:

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

Compile it with scalac and look at the class file with javap:

用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

}

The constructor is interesting. It tells us, that an array of ints is created (l. 12) which is filled with 1, 2 and 3. (l. 14-25). After that this array is delivered to scala.Predef$.wrapIntArray (l. 26). This resulting scala.collection.mutable.WrappedArray is again delivered to our List (l. 29). At the end, the List is stored in the field (l. 32). When we wanna create a List in Java, we have to do the same:

构造函数是有趣的。它告诉我们,创建了一个ints数组(l. 12),其中填充了1、2和3。(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 }));

This looks ugly, but it works. If you create a nice looking library which wraps the access to the Scala library it will be easy to use Scala from Java.

这看起来很丑,但确实有效。如果您创建了一个外观漂亮的库来封装对Scala库的访问,那么从Java使用Scala就很容易了。

Summary

I know there are some more rules how Scala code is compiled to bytecode. But I think with the information above it should be possible to find these rules by yourself.

我知道Scala代码如何编译成字节码还有一些规则。但是我认为有了上面的信息,你就可以自己找到这些规则了。

#2


15  

I'm not competing with the other answer, but since people seem often not to notice this, you can do this in the repl.

我并不是在与另一个答案竞争,但是由于人们似乎常常没有注意到这一点,你可以在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