Java <->斯卡拉interop:透明列表和地图转换
我正在学习Scala,我有一个Java项目迁移到斯卡拉。我想通过逐个重写类并检查新类没有破坏项目来迁移它。Java <->斯卡拉interop:透明列表和地图转换
该Java项目使用大量的java.util.List
和java.util.Map
。在新的Scala课程中,我希望使用Scala的List
和Map
以获得好看的Scala代码。
问题是,新类(这些是Scala中的wtitten)没有与现有Java代码无差别地集成在一起:Java需要java.util.List
,Scala需要它自己的scala.List
。
这是一个简化的问题示例。有类主,逻辑,道。他们互相呼叫:Main - > Logic - > Dao。
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很酷的功能中受益匪浅。
我需要重写类逻辑 Scala中,同时保留与类主要和道完整性。最好重写会是什么样子(不工作):
class Logic2 {
def calculate(ints: List[Integer]) : List[Integer] = {
val together: List[Integer] = new Dao().getSomeInts()
together ++ ints
}
}
理想行为:列表内Logic2是本地斯卡拉列出。全部进出java.util.Lists
自动获得装箱/取消装箱。但这不起作用。
相反,这样没有问题(感谢scala-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和Java之间的列表和地图的透明魔术转换 - > Scala(不需要做toScala/toJava)?
如果不可能,迁移Java的最佳实践是什么 - >使用java.util.List
和朋友的Scala代码是什么?
相信我;你不要想要来回透明转换。这正是scala.collection.jcl.Conversions
函数试图去做的。在实践中,它会引起很多头痛。
这种方法问题的根源在于,Scala会根据需要自动注入隐式转换以使方法调用工作。这可能会带来一些非常不幸的后果。例如:
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
if和只有如果你愿意有A <: B
(<:
表示“子类型”)。
由于java.util.Map
显然不是一个与我们的层次结构中的任何东西无关的新类型,我们不能置于第一个限制条件之下。因此,我们唯一的希望是我们的转换Map[A, B] => java.util.Map[A, B]
有资格获得第二个。但是,Scala的Map
继承自java.util.Map
绝对没有意义。它们是完全正交的接口/特征。如上所示,试图忽略这些准则几乎总是会导致奇怪和意外的行为。
事实是,javautils asScala
和asJava
方法被设计来解决这个确切的问题。从Map[A, B] => RichMap[A, B]
开始,在javautils中有一个隐式转换(实际上有一些转换)。 RichMap
是由javautils定义的全新类型,因此其唯一目的是将成员添加到Map
。特别是,它增加了asJava
方法,该方法返回一个包装图,该包装图实现了java.util.Map
,并委托给您的原始Map
实例。这使得这个过程更加明确,并且更不容易出错。
换句话说,使用asScala
和asJava
是的最佳实践。在生产应用程序中独立完成了这两条路,我可以直接告诉你,javautils方法更安全,更容易处理。不要试图绕过它的保护,仅仅是为了拯救你自己的8个角色!
下面是使用豪尔赫·奥尔蒂斯scalaj-collection library一些简单的例子:
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项目可从central maven repository
使用Scala 2.8,这是可以做到这样的:
import scala.collection.JavaConversions._
val list = new java.util.ArrayList[String]()
list.add("test")
val scalaList = list.toList
非常好,+1 – geowa4 2009-10-05 15:33:31
(+1)非常好地陈述。我希望这篇文章已经出现,当我在一段时间后与之奋斗时。 – Shaun 2009-10-05 15:46:57
感谢您的详细报道。通过谷歌找到了(当然!)所以已经救了一个人很多的痛苦! – 2009-10-15 06:59:36