如何使用Java而不是Java来学习如何使用scala. [英] How to learn about using scala.None from Java using javap?

查看:98
本文介绍了如何使用Java而不是Java来学习如何使用scala.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在上一个问题中, 访问scala./em>,似乎人们已经使用javap来弄清楚如何从Java访问scala.None.我想知道他们是如何做到的.仅供参考,答案是:

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);

可以缩写为:

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.Nonescala.Option上运行了javap.如何从javap输出中得出以下结论:

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

  1. None是扩展了Option
  2. None.type类型的唯一对象.
  3. 需要伴随对象的apply()方法
  1. None is a sole object of None.type type which extends Option
  2. The apply() method for the companion object is required

?

推荐答案

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

注意:在撰写本文时,我注意到javac和eclipse-javac在从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.

类,构造函数,方法

这里没有特殊规则.以下Scala类

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
}

可以像普通的Java类一样访问

.它被编译为一个名为X.class:

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.

对于一个类class X(var i: Int)将创建Getter和Setter.对于类class X(val i: Int),仅创建一个Getter:

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

注意,在Java中,标识符不允许包含特殊符号.因此,scalac为这些特殊符号中的每一个生成一个特定的名称.有一个类 scala.reflect.NameTransformer 可以对操作进行编码/解码:

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)

通过与在构造函数中创建字段时相同的模式来翻译类class X { var i = 5 }.无法从Java直接访问变量i,因为它是私有的.

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.

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

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();

案例分类

Scala编译器会自动为case类生成一个应用方法,并为字段自动生成Getter.案例类case class X(i: Int)易于访问:

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

特质

仅包含抽象成员的特征trait T { def m }被编译为接口,该接口放置在名为T.class的类文件中.因此,它可以通过Java类轻松实现:

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
  }
}

如果特征包含具体成员,则除了正常接口外,还会生成一个名为<trait_name>$class.class的类文件.特质

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
}

也可以在Java中轻松实现.类文件T$class.class包含该特征的具体成员,但似乎无法从Java访问它们. javac和eclipse-javac都不会编译对此类的访问.

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.

函数文字被编译为FunctionN类的匿名实例.一个Scala对象

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
  }
}

如上所述,

被编译为普通的类文件.此外,每个函数文字都有其自己的类文件.因此,对于函数值,将生成一个名为<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.

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
  • i和j的
  • X$$anonfun$i$1.classX$$anonfun$i$1$$anonfun$j$1$1.class
  • 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:

  • 普通的Scala类可由其构造函数或其应用方法访问
  • 当没有构造函数时,就会有一个套用方法
  • 当没有构造函数且没有apply方法时,存在另一个类文件,该类文件的名称与调用类的方式相同,该类文件的末尾附加一个美元符号.在此类中搜索MODULE$字段
  • 构造函数和apply-method是继承的,因此如果您在子类中找不到任何内容,请搜索超类
  • 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
// 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方法可以使用:

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);

有些

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

它有一个构造函数和一个应用方法(因为我们知道Option有一个,而Some扩展了Option).使用其中之一并开心:

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);

没有

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

它没有构造函数,没有套用方法,也没有扩展Option.因此,我们来看看None$:

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$();
}

是的!我们找到了一个MODULE$字段和Option的apply方法.此外,我们找到了私有构造函数:

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

列表

scala.collection.immutable.List是抽象的,因此我们必须使用scala.collection.immutable.List$.它有一个应用方法,期望scala.collection.Seq.因此,要获取列表,我们首先需要一个Seq.但是,如果我们期待Seq,则没有适用方法.此外,当我们查看Seq和scala.collection.Seq$的超类时,我们只能找到期望Seq的应用方法.那么,该怎么办?

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?

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

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)
}

使用scalac进行编译,并使用javap查看类文件:

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

}

构造函数很有趣.它告诉我们,创建了一个整数数组(l.12),其中填充有1、2和3(l.14-25).此后,此数组将传递到scala.Predef$.wrapIntArray(图26).得到的scala.collection.mutable.WrappedArray再次传送到我们的列表中(图29).最后,列表存储在字段中(图32). 当我们想用Java创建一个List时,我们必须做同样的事情:

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 }));

这看起来很丑,但是行得通.如果您创建一个美观的库来包装对Scala库的访问,那么使用Java中的Scala将会很容易.

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

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.

这篇关于如何使用Java而不是Java来学习如何使用scala.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆