我学习Scala和我有一个Java项目迁移到Scala的。 我想通过重写类一个接一个,并检查新的类没有突破项目迁移它。
该Java项目使用大量java.util.List
和java.util.Map
。 在新的Scala类我想用Scala的List
和Map
有好看的Scala代码。
问题是,新的类(那些在斯卡拉wtitten)不与现有的Java代码seamelessly集成:Java需要java.util.List
,斯卡拉需要自己scala.List
。
这是问题的简化示例。 有类主 , 逻辑 , 道 。 他们称对方在一条线上: 首页- >逻辑- >道 。
public class Main {
public void a() {
List<Integer> res = new Logic().calculate(Arrays.asList(1, 2, 3, 4, 5));
}
}
public class Logic {
public List<Integer> calculate(List<Integer> ints) {
List<Integer> together = new Dao().getSomeInts();
together.addAll(ints);
return together;
}
}
public class Dao {
public List<Integer> getSomeInts() {
return Arrays.asList(1, 2, 3);
}
}
在我的情况, 主要的类与道是框架类(我不需要迁移它们)。 类逻辑是业务逻辑和将受益匪浅斯卡拉很酷的功能。
我需要重写类逻辑 Scala中,同时保留与类主要与道的完整性。 最好重写会是什么样子(不工作):
class Logic2 {
def calculate(ints: List[Integer]) : List[Integer] = {
val together: List[Integer] = new Dao().getSomeInts()
together ++ ints
}
}
理想的行为:内部Logic2列表是本地斯卡拉列表。 所有输入/输出java.util.Lists
得到盒装/拆箱自动的。 但是,这是行不通的。
取而代之的是,这并不工作(感谢斯卡拉- javautils ( GitHub上 )):
import org.scala_tools.javautils.Implicits._
class Logic3 {
def calculate(ints: java.util.List[Integer]) : java.util.List[Integer] = {
val together: List[Integer] = new Dao().getSomeInts().toScala
(together ++ ints.toScala).toJava
}
}
但它看起来丑陋。
如何实现Java之间的透明的魔法转换列表和地图< - >斯卡拉(无需做toScala / toJava)?
如果它是不可能的,什么是Java的迁移的最佳实践-使用> Scala代码java.util.List
和朋友吗?
相信我; 你不想透明的来回转换。 这也恰恰是scala.collection.jcl.Conversions
功能试图这样做。 在实践中,它会导致很多麻烦。
这种方法的问题的根源是斯卡拉将自动注入必要的隐式转换,使一个方法调用工作。 这可以有一些非常不幸的后果。 例如:
import scala.collection.jcl.Conversions._
// adds a key/value pair and returns the new map (not!)
def process(map: Map[String, Int]) = {
map.put("one", 1)
map
}
此代码不会完全性格的人谁是新的Scala集合框架,甚至只是一成不变的集合的概念。 不幸的是,这是完全错误的。 这个函数的结果是相同的地图。 到呼叫put
触发的隐式转换到java.util.Map<String, Int>
,其愉快地接受新的值,并且立即被丢弃。 原来map
是未经修改的(因为它确实是不可变的)。
时,他说,你应该只定义隐式转换为两个目的一个豪尔赫·奥尔蒂斯说得最好:
- 添加成员(方法,字段等)。 这些转换应该是一个新的类型无关的范围别的。
- “修复”一个破碎的类层次结构。 因此,如果你有一些类型的
A
和B
这是不相关的。 可以定义一个转换A => B
当且仅当你会优选具有A <: B
( <:
指“亚型”)。
由于java.util.Map
显然不是一个新的类型无关,我们的层次东西,我们不能落在第一个条件下。 因此,我们唯一的希望是我们转换Map[A, B] => java.util.Map[A, B]
以符合第二个。 然而,这使得完全没有意义的Scala的Map
要继承java.util.Map
。 他们是真正的完全正交接口/特质。 如上所证实的,试图忽略这些准则将几乎总是导致怪异和意外的行为。
事实是,在javautils asScala
和asJava
方法旨在解决这一确切的问题。 有一个从在javautils的隐式转换(其中一些实际上) Map[A, B] => RichMap[A, B]
RichMap
是javautils定义一个全新的类型,所以它的唯一目的是为了添加成员Map
。 特别是,它增加了asJava
方法,它返回它实现了一个包装地图java.util.Map
和代表到原来的Map
实例。 这使得过程更加明确,远远不容易出错。
换句话说,使用asScala
和asJava
是最好的做法。 已经走了下来,这些道路都独立于生产中的应用,我可以告诉你第一手的javautils方法是更安全,更容易使用。 不要尝试只规避其保护为自己节省8个字符的缘故!
下面是使用豪尔赫·奥尔蒂斯的一些简单的例子scalaj收集库 :
import org.scala_tools.javautils.Implicits._
val sSeq = java.util.Collections.singletonList("entry") asScala
// sSeq: Seq[String]
val sList = sSeq toList // pulls the entire sequence into memory
// sList: List[String]
val sMap = java.util.Collections.singletonMap("key", "value") asScala
// sMap: scala.collection.Map[String, String]
val jList = List("entry") asJava
// jList: java.util.List[String]
val jMap = Map("key" -> "value") asJava
// jMap: java.util.Map[String, String]
该项目javautils可从中央Maven仓库
使用Scala 2.8,这是可以做到这样的:
import scala.collection.JavaConversions._
val list = new java.util.ArrayList[String]()
list.add("test")
val scalaList = list.toList