如何学习有关使用scala的信息。无法从Java使用javap?(How to learn about using scala.None from Java using javap?)

编程入门 行业动态 更新时间:2024-10-28 00:16:13
如何学习有关使用scala的信息。无法从Java使用javap?(How to learn about using scala.None from Java using javap?)

在前面的问题中, 从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

我也在scala.None和scala.Option上运行javap。 如何从javap输出中找出:

None是None类型的唯一对象,它扩展了Option 伴随对象的apply()方法是必需的

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 of None.type type which extends Option The apply() method for the companion object is required

?

最满意答案

有一些规则是如何将Scala代码编译为JVM字节码的。 由于潜在的名称冲突,生成的代码并不总是直观易懂,但是如果知道规则,就可以访问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);

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

字段,值

对于类class X(var i: Int) ,将创建Getters和Setters。 对于类class 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为每个特殊符号生成一个特定名称。 有一个类scala.reflect.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)

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

对象

在Java中没有像Scala对象那样的东西。 因此Scalac必须做一些魔术。 对于对象object X { val i = 5 }将生成两个JVM类文件: X.class和X$.class 。 第一个像接口一样工作,它包含静态方法来访问Scala对象的字段和方法。 后者是一个单例类,不能实例化。 它有一个字段,它包含类的单例实例,名为MODULE$ ,它允许访问单例:

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

案例类

Scala编译器自动为案例类和字段的Getters生成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都不会编译对这个类的访问。

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

功能

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

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

这意味着在我们的情况下我们得到:

X$$anonfun$1.class for f X$$anonfun$g$1.class for g X$$anonfun$h$1$$anonfun$apply$1.class for h X$$anonfun$i$1.class和X$$anonfun$i$1$$anonfun$j$1$1.class for i and j

要访问它们,请使用他们的apply-method:

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

回答问题

你应该知道:

一个普通的Scala类可以通过它们的构造函数或其应用方法来访问 当没有构造函数而不是应用方法时 当没有构造函数和没有应用方法时,就会有另一个类文件命名方式相同,该类会在最后附加一个美元符号。 在这个类中搜索一个MODULE$字段 构造函数和apply-methods都是继承的,所以如果在子类中找不到任何东西,那么搜索超类

一些例子

选项

// 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方法。 此外,它说这个班是抽象的。 因此只能使用apply方法:

Option.apply(3);

一些

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

它有一个构造函数和一个apply-method(因为我们知道Option有一个和一些extends选项)。 使用其中之一,并开心:

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

没有

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

它没有构造函数,没有apply-method,也没有扩展Option。 所以,我们会看看None$ :

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

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

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$ 。 它有一个应用程序方法,期望scala.collection.Seq 。 所以要得到一个List,我们首先需要一个Seq。 但是如果我们看看Seq,就没有应用方法。 此外,当我们查看Seq的超类和scala.collection.Seq$我们只能找到一个期望Seq的apply-methods。 那么该怎么办?

我们必须看看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 }

构造函数很有趣。 它告诉我们,创建了一个整数(l.12),其中填充了1,2和3(l.14-25)。 之后,这个数组被传递给scala.Predef$.wrapIntArray ( scala.Predef$.wrapIntArray )。 产生的scala.collection.mutable.WrappedArray再次传递给我们的List( scala.collection.mutable.WrappedArray )。 最后,列表存储在该字段中(l.32)。 当我们想要在Java中创建一个List时,我们必须这样做:

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代码编译为字节码。 但我认为,根据上面的信息,您应该可以自己找到这些规则。

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.

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.

Classes, Constructors, Methods

There are no special rules here. The following Scala class

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:

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:

//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:

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.

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:

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:

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:

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

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.

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

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.

This means in our case we get:

X$$anonfun$1.class for f X$$anonfun$g$1.class for g X$$anonfun$h$1$$anonfun$apply$1.class for h X$$anonfun$i$1.class and X$$anonfun$i$1$$anonfun$j$1$1.class for i and 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 when there is no constructor than there is an 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 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:

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:

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$:

// 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?

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

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

Compile it with scalac and look at the class file with 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:

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.

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.

更多推荐

本文发布于:2023-07-21 09:41:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1209027.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:信息   scala   Java   learn   javap

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!