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.Option$.MODULE$.apply(null);
which can be shorted to:
scala.Option.apply(null);
Given this program (OptionTest.scala
):
object OptionTest extends App {
val x = scala.None
val y = scala.Some("asdf")
}
I ran javap
on it like this:
javap -s -c -l -private OptionTest
This is a portion of the javap
output:
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:
None
is a sole object ofNone.type
type which extendsOption
- The
apply()
method for the companion object is required
?
I'm not competing with the other answer, but since people seem often not to notice this, you can do this in the repl.
There are rules how Scala code is compiled to JVM-bytecode. Because of potential name clashes 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.
Classes, Constructors, Methods
There are no special rules here. The following Scala class
can be accessed like a normal Java class. It is compiled to a file named
X.class
: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 classclass X(val i: Int)
only a Getter is created: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:
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 variablei
from Java is not possible, because it is private.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
andX$.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, namedMODULE$
, which allows access to the singleton: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:Traits
A trait
trait T { def m }
, which contains only abstract members, is compiled to an interface, which is placed in a class files namedT.class
. Therefore it can easily implemented by a Java class:If the trait contains concrete members there is a class file named
<trait_name>$class.class
generated, additionally to the normal interface. The traitcan 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.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
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 theanonfun
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 inh
it gets the nameapply
.This means in our case we get:
X$$anonfun$1.class
for fX$$anonfun$g$1.class
for gX$$anonfun$h$1$$anonfun$apply$1.class
for hX$$anonfun$i$1.class
andX$$anonfun$i$1$$anonfun$j$1$1.class
for i and jTo access them use their apply-method:
Answer the question
You should know:
MODULE$
fieldSome examples
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:
Some
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:
None
It has no constructor, no apply-method and doesn't extend Option. So, we will take a look to
None$
:Yeah! We found a
MODULE$
field and the apply-method of Option. Furthermore we found the private constructor:List
scala.collection.immutable.List
is abstract, thus we have to usescala.collection.immutable.List$
. It has an apply-method which expects anscala.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 atscala.collection.Seq$
we can only find an apply-methods which expects a Seq. So, what to do?We have to take a look how scalac creates an instance of List or Seq. First create a Scala class:
Compile it with scalac and look at the class file with javap:
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 resultingscala.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: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.
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.