Java的< - > Scala的互操作性:透明的列表和地图转换(Java <->

2019-07-20 22:02发布

我学习Scala和我有一个Java项目迁移到Scala的。 我想通过重写类一个接一个,并检查新的类没有突破项目迁移它。

该Java项目使用大量java.util.Listjava.util.Map 。 在新的Scala类我想用Scala的ListMap有好看的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和朋友吗?

Answer 1:

相信我; 你不想透明的来回转换。 这也恰恰是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是未经修改的(因为它确实是不可变的)。

时,他说,你应该只定义隐式转换为两个目的一个豪尔赫·奥尔蒂斯说得最好:

  • 添加成员(方法,字段等)。 这些转换应该是一个新的类型无关的范围别的。
  • “修复”一个破碎的类层次结构。 因此,如果你有一些类型的AB这是不相关的。 可以定义一个转换A => B当且当你会优选具有A <: B<:指“亚型”)。

由于java.util.Map显然不是一个新的类型无关,我们的层次东西,我们不能落在第一个条件下。 因此,我们唯一的希望是我们转换Map[A, B] => java.util.Map[A, B]以符合第二个。 然而,这使得完全没有意义的Scala的Map要继承java.util.Map 。 他们是真正的完全正交接口/特质。 如上所证实的,试图忽略这些准则将几乎总是导致怪异和意外的行为。

事实是,在javautils asScalaasJava方法旨在解决这一确切的问题。 有一个从在javautils的隐式转换(其中一些实际上) Map[A, B] => RichMap[A, B] RichMap是javautils定义一个全新的类型,所以它的唯一目的是为了添加成员Map 。 特别是,它增加了asJava方法,它返回它实现了一个包装地图java.util.Map和代表到原来的Map实例。 这使得过程更加明确,远远不容易出错。

换句话说,使用asScalaasJava 最好的做法。 已经走了下来,这些道路都独立于生产中的应用,我可以告诉你第一手的javautils方法是更安全,更容易使用。 不要尝试只规避其保护为自己节省8个字符的缘故!



Answer 2:

下面是使用豪尔赫·奥尔蒂斯的一些简单的例子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仓库



Answer 3:

使用Scala 2.8,这是可以做到这样的:

import scala.collection.JavaConversions._

val list = new java.util.ArrayList[String]()
list.add("test")
val scalaList = list.toList


文章来源: Java <-> Scala interop: transparent List and Map conversion