如何在Java中实现Scala apply方法

Jer*_*sen 9 java scala

我想从Scala代码中调用一些Java代码.我想使用Scala的apply构造,所以我可以像这样调用它:

val led = OutPin(0)
Run Code Online (Sandbox Code Playgroud)

代替:

val led = new OutPin(0)
Run Code Online (Sandbox Code Playgroud)

我天真地在我的Java代码中实现了一个额外的apply方法,如下所示:

public class OutPin {

    public OutPin(int pinNumber) {
    }

    public OutPin apply(int pinNumber) {
        return new OutPin(pinNumber);
    }
}
Run Code Online (Sandbox Code Playgroud)

这不会使我的Scala代码(上面的第一行)编译,而是给我一个错误:

对象OutPin不是值

在Java中实现Scala的apply方法的正确方法是什么?

Ste*_*man 14

您的问题不是使用apply方法本身,而是尝试在Java中实现Scala单例对象.

我认为(但不确定)这是非常困难的,甚至是不可能的.

考虑一个非常非常简单的案例:

object Obj;
Run Code Online (Sandbox Code Playgroud)

这将编译为两个JVM bytcode文件,Obj$.classObj.class.从理论上讲,只检查这两个类的字节码应该很容易,并在Java中重新表达同样的东西.Scala单例对象的基本结构非常非常简单:

  1. 对于一个单对象Obj,一个Obj.classObj$.class必须产生
  2. Obj$类必须有一个public final static类型的字段Obj$称为MODULE$,这将在类的初始化初始化指的是单独的对象.在Scala中,调用Obj.foo()被映射到Obj$.MODULE$.foo()[...如果Obj有一个方法被调用foo(),那就是!]
  3. Java编译器对这些Scala生成的类对一无所知,因此对于Java interop,Obj该类包含静态函数,这些函数只转发给具有相同名称和签名的方法的调用Obj$.MODULE$.

这听起来很复杂,但实际上并非如此.编写一对迄今为止的Java类是微不足道的.但是Scala编译器(2.10.3)仍然不会将该对识别为构成Scala单例.深入研究Scala编译器生成的单例的字节码,您将看到在合法Java中难以表达的细节.[提示:javap -c -p -s -v <fully-qualified-class-name>]

例如,最终的静态MODULE$字段由静态初始化程序间接初始化.静态初始化程序只构造一个Obj$对象,而不直接赋值.赋值发生在私有构造函数中.这在Java中是非法的:空白静态终结必须在静态初始化程序中进行初始化,并且不能在可能从初始化程序外部多次调用的代码(如私有构造函数)中分配.Scala编译器生成尊重空白最终语义的字节码(因为私有构造函数只调用一次),但超出了Java编译器验证这些语义的能力.因此,如果用Java表示,此代码将被拒绝.

此外,Obj类(没有终端美元符号的版本)包含ScalaSig类型的注释,它看起来相当复杂并且难以手工复制(在Java或Scala中),至少我们这些人不确定这是怎么回事注释有效.

在决定将一对类视为"值"之前,我不确切知道Scala编译器的用途是什么,这是一个有效的Scala单例对象,但Scala的设计者选择不让它变得简单,尽管简单基本方案.可能他们希望保留重新组织scala单例对象转换为字节码的能力.让Java程序员合成scala单例对象将有效地使当前方案成为Scala公共API的永久部分.

请注意,编写一个普通的非单例类,其实例具有apply(...)方法,因此可以像Java一样调用,并且工作正常.这是一只Java猫:

public class Cat {
    public String apply( int i ) {
        return "Meow: " + i;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是使用Scala的加糖应用:

Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val Morris = new Cat;
Morris: Cat = Cat@6b4feafa

scala> Morris(8)
res0: String = Meow: 8
Run Code Online (Sandbox Code Playgroud)