如何在Java中实现Scala apply方法
我想从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方法的正确方法是什么
# 1 楼答案
如果你想要一个在Scala中可用的apply方法,你应该在Scala端实现它,使用
object
将java类与实例化它的类同名# 2 楼答案
问题是Scala编译器说必须在同一个文件中定义两个伴生对象,否则会出现错误:
下面是另一种可能对你有用的方法。如果在一个单独的包中定义Scala
OutPin
,那么它就可以工作了。例如:Java类:
Scala类:
Scala客户端示例:
运行客户端打印
1
要访问含糖的
apply
,您必须导入base.scala
,而不仅仅是base
。如果获得apply语法真的很重要,那么它可能是值得的# 3 楼答案
您的问题不在于apply方法本身,而在于试图在Java中实现Scala单例对象
我认为(但不确定)这是非常困难的,甚至是不可能的
考虑一个非常非常简单的例子:
这将编译成两个JVM字节码文件
Obj$.class
和Obj.class
。理论上,只需检查这两个类的字节码,并在Java中重新表达相同的内容,应该很容易。Scala singleton对象的基本结构非常简单:Obj
,必须生成Obj.class
和Obj$.class
Obj$
类必须有一个public final static
类型的Obj$
字段,名为MODULE$
,它将在类初始化时初始化,并引用singleton对象。在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中是非法的:blank static final肯定必须在静态初始值设定项中初始化,并且不能在可能从初始值设定项外部多次调用的代码(如私有构造函数)中赋值。Scala编译器生成字节码,它尊重空的最终语义(因为只调用一次私有构造函数),但超过了java编译器验证这些语义的能力。因此,如果用Java表示,这段代码将被拒绝另外,
Obj
类(没有终端美元符号的版本)包含一个类型为ScalaSig的注释,它看起来非常复杂,很难手动复制(在Java或Scala中),至少对于我们这些不确定该注释是如何工作的人来说是这样在决定将一对类视为“值”之前,我不知道Scala编译器到底在寻找什么,这是一个有效的Scala单例对象,但Scala的设计人员选择了不让它变得简单,尽管基本方案很简单。他们可能希望保留重新组织scala单例对象如何转换为字节码的能力。让Java程序员合成scala单例对象将有效地使当前方案成为scala公共API的永久部分
请注意,编写一个普通的非单例类,其实例具有apply(…)方法,因此可以像函数一样调用,这在Java中很容易,而且工作良好。下面是一只爪哇猫:
以下是Scala的糖化apply的用法: