【问题标题】:Java <-> Scala interop: transparent List and Map conversionJava <-> Scala 互操作:透明的 List 和 Map 转换
【发布时间】:2009-10-05 12:40:34
【问题描述】:

我正在学习 Scala,并且我有一个要迁移到 Scala 的 Java 项目。我想通过一个接一个地重写类并检查新类没有破坏项目来迁移它。

这个 Java 项目使用了很多 java.util.Listjava.util.Map。在新的 Scala 类中,我想使用 Scala 的 ListMap 来获得好看的 Scala 代码。

问题在于新类(在 Scala 中编写)不能与现有 Java 代码无缝集成:Java 需要 java.util.List,Scala 需要自己的 scala.List

以下是问题的简化示例。有MainLogicDao类。他们在一行中互相调用: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);
    }
}

在我的情况下,类 MainDao 是框架类(我不需要迁移它们)。 Logic 类是业务逻辑类,将受益于 Scala 的酷炫特性。

我需要在 Scala 中重写类 Logic,同时保持类 MainDao 的完整性。最好的重写看起来像(不起作用):

class Logic2 {
  def calculate(ints: List[Integer]) : List[Integer] = {
      val together: List[Integer] = new Dao().getSomeInts()
      together ++ ints
  }
}

理想行为:Logic2 中的列表是原生 Scala 列表。全部输入/输出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 Scala之间Lists和Maps的透明魔法转换(无需toScala/toJava)?

如果不可能,迁移 Java -> 使用 java.util.List 和朋友的 Scala 代码的最佳做法是什么?

【问题讨论】:

    标签: java scala interop scala-java-interop


    【解决方案1】:

    相信我;你不想要透明的来回转换。这正是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 集合框架甚至不可变集合概念的人来说,这段代码不会完全不合时宜。不幸的是,这是完全错误的。此函数的结果是 same 映射。对put 的调用会触发到java.util.Map&lt;String, Int&gt; 的隐式转换,它会愉快地接受新值并立即丢弃。原始的 map 未修改(因为它确实是不可变的)。

    Jorge Ortiz 说得最好,他说您应该只为以下两个目的之一定义隐式转换:

    • 添加成员(方法、字段等)。这些转换应该是一个新的类型无关范围内的任何其他东西。
    • “修复”损坏的类层次结构。因此,如果您有一些不相关的类型 AB。如果您希望使用A &lt;: B&lt;: 表示“子类型”),您可以定义转换A =&gt; B,并且

    由于java.util.Map 显然不是与我们层次结构中的任何东西无关的新类型,我们不能属于第一个附带条件。因此,我们唯一的希望是我们的转换Map[A, B] =&gt; java.util.Map[A, B] 有资格获得第二个。然而,Scala 的Map 继承自java.util.Map 绝对没有意义。它们实际上是完全正交的接口/特征。如上所示,试图忽略这些准则几乎总是会导致奇怪和意外的行为。

    事实上,javautils asScalaasJava 方法旨在解决这个确切的问题。在 Map[A, B] =&gt; RichMap[A, B] 的 javautils 中有一个隐式转换(实际上是其中的一些)。 RichMap 是 javautils 定义的全新类型,所以它的唯一目的就是给Map 添加成员。特别是,它添加了asJava 方法,该方法返回一个实现java.util.Map 的包装器映射并委托给您原来的Map 实例。这使得该过程更加明确且不易出错。

    换句话说,使用asScalaasJava 最佳实践。在生产应用程序中独立地走完这两条路后,我可以直接告诉您 javautils 方法更安全、更易于使用。不要仅仅为了节省 8 个字符而试图绕过它的保护!

    【讨论】:

    • (+1) 说得非常好。我希望这篇文章在我前段时间与它作斗争时就已经存在了。
    • 感谢您写得如此详尽。通过谷歌找到这个(当然!)所以已经为某人节省了很多痛苦!
    • 注意,javautils 已被取代。这些天你想要github.com/scalaj/scalaj-collection
    • 我相信现在应该使用 JavaConversions 而不是 jcl.Conversions。
    • 从 2.12 开始,它是 import scala.collection.JavaConverters._
    【解决方案2】:

    以下是一些使用 Jorge Ortiz 的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 获得

    【讨论】:

      【解决方案3】:

      使用 Scala 2.8,可以这样完成:

      import scala.collection.JavaConversions._
      
      val list = new java.util.ArrayList[String]()
      list.add("test")
      val scalaList = list.toList
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-20
        • 2013-07-01
        • 2013-04-11
        • 1970-01-01
        • 1970-01-01
        • 2021-02-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多