12 August 2011

Scala调用Java,完全没有问题;但是反过来总有那么一点不爽,或者必须要修改。这里是项目中一个真实的案例,拿出来分享一下。

  1. 类型转换
1
2
3
4
5
object AsnLoader {
  ...
  def batchLoadFile(files: List[String]): Map[String, AsnModule] = files.foldLeft(Map[String, AsnModule]())((x, y) => x ++ load(y))
  ...
}

这里的List是Scala里的类,从Java里面调这个方法是不行的,所以需要弄一个wrapper方法:

1
2
3
import collection.JavaConversions._
...
def batchLoadFile(files: java.util.List[String]): java.util.Map[String, AsnModule] = batchLoadFile(asScalaBuffer(files).toList)

虽然asScalaBuffer本身就是个implicit,但是这里仍需要显示调用一下,否则编译器会认为batchLoadFile(file: java.util.List[String])更符合调用匹配规则,结果就变成了无限递归……

Java里面这样调用:

1
AsnLoader.batchLoadFile(list);

这样看起来解决问题了,但是Eclipse的Scala插件会报一个莫名其妙的函数签名错误,其实根本就没错,直接maven或者用Intellij都是对的,Eclipse插件太烂或者太老。无奈只好把函数名也一起改了:batchLoadFileJ,这样就可以了。如果还不是很明白,去看看JavaConversions里面的implicit。

把Java的Map转换成Scala的immutale map稍微曲折了些,先转换成mutable map,再调用toMap方法转换成immutable map。

  1. 构造方法
1
2
3
4
5
class DefaultTransformer(private val asnModule: AsnModule, private val dependencies: Map[String, AsnModule] = Map.empty,
                         private val repository: Map[String, AsnModule] = Map.empty,
                         private val messageHandler: MessageHandler = ConsoleMessageHandler(false)) {
  ...
}

这里面的Map都是Scala的Map,所以从Java里不能直接调用,而Scala的构造方法又比较特别,重载的话只能减少参数个数而不能改变类型。这时候就需要companion object上场了。

1
2
3
4
5
6
object DefaultTransformer {
  def apply(asnModule: AsnModule,
                 dependencies: java.util.Map[String, AsnModule],
                 repository: java.util.Map[String, AsnModule],
                 messageHandler: MessageHandler) = new DefaultTransformer(asnModule, dependencies, repository, messageHandler)
}

Java这样调用:

1
Transformer transformer = DefaultTransformer.apply(...);
  1. Scala的bug

嗯,XML在什么语言里都是让人恶心的东西,即使Scala已经尽力做好了:https://issues.scala-lang.org/browse/SI-4865



blog comments powered by Disqus