gpt4 book ai didi

scala - 如何使用Java而不是Java来学习如何使用scala。

转载 作者:行者123 更新时间:2023-12-03 08:33:25 28 4
gpt4 key购买 nike

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

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输出中得出以下结论:
  • NoneNone.type类型的唯一对象,它扩展了Option
  • 配套对象的apply()方法是必需的

  • 最佳答案

    有规则将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.



    类,构造函数,方法

    这里没有特殊规则。以下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)类,将创建Getter和Setter。对于 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,因为它是私有(private)的。

    对象

    Java中没有Scala对象。因此,scalac必须做一些魔术。对于对象 object X { val i = 5 },将生成两个JVM类文件: X.classX$.class。第一个类似于接口(interface),它包含用于访问字段和Scala对象的方法的静态方法。后者是无法实例化的单例类。它具有一个Field,该字段保存名为 MODULE$的类的单例实例,该实例允许访问单例:
    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 }被编译为接口(interface),该接口(interface)放置在名为 T.class的类文件中。因此,它可以通过Java类轻松实现:
    class X implements T {
    public void m() {
    // do stuff here
    }
    }

    如果特征包含具体成员,则除了正常接口(interface)外,还会生成一个名为 <trait_name>$class.class的类文件。特质
    trait T {
    def m1
    def m2 = 5
    }

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

    可以在 here中找到有关如何编译特征的更多详细信息。

    职能

    函数文字被编译为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
  • g的X$$anonfun$g$1.class
  • H 的
  • X$$anonfun$h$1$$anonfun$apply$1.class I和J的
  • X$$anonfun$i$1.classX$$anonfun$i$1$$anonfun$j$1$1.class

  • 要访问它们,请使用其apply方法:
    X.f().apply(7);
    X.g().apply(7);
    X.h().apply(3).apply(5);
    X.i().apply(3).apply(5);

    回答问题

    你应该知道:
  • 正常的Scala类可以由其构造函数或其应用方法访问
  • (如果没有构造函数,而不是应用方法)
  • (如果没有构造函数且没有apply方法),则存在另一个名为相同类的类文件,该类文件的调用方式与在类末尾附加一个美元符号的方式相同。在此类中搜索MODULE$字段
  • 构造函数和apply-method是继承的,因此,如果您在子类
  • 中找不到任何内容,请搜索父类(super class)

    一些例子

    选项
    // 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);
    }

    它具有一个构造函数和一个应用方法(因为我们知道Option具有一个,而Some扩展了Option)。使用其中之一并感到高兴:
    new Some<Integer>(3);
    Some.apply(3);

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

    它没有构造函数,没有套用方法,也没有扩展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的apply方法。此外,我们找到了私有(private)构造函数:
    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是抽象的,因此我们必须使用 scala.collection.immutable.List$。它有一个应用方法,需要 scala.collection.Seq。因此,要获取列表,我们首先需要一个Seq。但是,如果我们看Seq,就没有适用方法。此外,当我们查看Seq的父类(super class)和 scala.collection.Seq$时,我们只能找到一个期望Seq的应用方法。那么该怎么办?

    我们必须看看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(图26)。生成的 scala.collection.mutable.WrappedArray再次传送到我们的列表中(图29)。最后,列表存储在字段中(图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代码编译为字节码。但是我认为,根据以上信息,您可以自己找到这些规则。

    关于scala - 如何使用Java而不是Java来学习如何使用scala。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9350528/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com