我想从Scala代码中调用一些Java代码。我想使用Scala的apply结构,所以我可以这样称呼它:
val led = OutPin(0)
代替:
val led = new OutPin(0)
我天真地在Java代码中实现了一个附加的apply方法,如下所示:
public class OutPin {
public OutPin(int pinNumber) {
}
public OutPin apply(int pinNumber) {
return new OutPin(pinNumber);
}
}
这不会编译我的Scala代码(上面的第一行),而是给了我一个错误:
对象OutPin不是值
用Java实现Scala的apply方法的正确方法是什么?
您的问题不是本质上的apply方法,而是试图在Java中实现Scala单例对象。
我认为(但不确定)这在设计上是非常困难的,甚至是不可能的。
考虑一个非常非常简单的情况:
object Obj;
这将编译为两个JVM bytcode文件Obj$.class
和Obj.class
。从理论上讲,只需检查这两个类的字节码,然后在Java中重新表达相同的内容,应该很容易。Scala单例对象的基本结构非常非常简单:
Obj
,一个Obj.class
和Obj$.class
必须产生Obj$
类必须有一个public final static
类型的字段Obj$
称为MODULE$
,这将在类初始化初始化指的是单独的对象。在Scala中,调用以Obj.foo()
映射到Obj$.MODULE$.foo()
[...如果Obj有一个名为的方法foo()
,那就是!]Obj
该类包含静态函数,这些静态函数只转发给具有相同名称和签名的方法的调用Obj$.MODULE$
。这听起来很复杂,但实际上并没有那么多。编写到目前为止已经完成的一对Java类很简单。但是Scala编译器(2.10.3)仍然不会将这对构成Scala单例。深入研究Scala编译器生成的单例的字节码,您会发现有些细节很难用合法的Java表达。[提示:javap -c -p -s -v <fully-qualified-class-name>
]
例如,最终的静态MODULE$
字段由静态初始化程序间接初始化。静态初始化器只是构造一个Obj$
对象,而无需直接分配它。分配发生在私有构造函数中。在Java中这是非法的:必须在静态初始值设定项中初始化空白的静态final,并且不能在可能从初始值设定项外部多次调用的代码(如私有构造函数)中进行分配。Scala编译器生成尊重空白最终语义的字节码(因为私有构造函数仅被调用一次),但超出了Java编译器验证那些语义的能力。因此,如果用Java表示,则该代码将被拒绝。
另外,Obj
该类(不带终端美元符号的版本)包含类型为ScalaSig的注释,该注释看起来相当复杂,并且难以手动复制(在Java或Scala中),至少对于我们中的那些人不确定注释工程。
在决定将一对类视为“值”(这是一个有效的Scala单例对象)之前,我不确切知道Scala编译器会寻找什么,但是尽管Scala的简单性,但Scala的设计师选择不使其变得简单。基本方案。他们可能希望保留重组scala单例对象如何转换为字节码的能力。让Java程序员合成Scala单例对象将有效地使当前方案成为Scala公共API的永久组成部分。
注意,编写一个普通的非单例类,其实例具有apply(...)方法,因此可以像调用函数一样从Java轻松进行并且工作正常。这是一只Java猫:
public class Cat {
public String apply( int i ) {
return "Meow: " + i;
}
}
这是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
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句