Java <-> Scala 互操作 : transparent List and Map conversion

标签 java scala interop scala-java-interop

我正在学习 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 和 friend 的 Scala 代码的最佳做法是什么?

最佳答案

相信我;你不想要透明的来回转换。这正是 scala.collection.jcl.Conversions试图做的功能。在实践中,它会引起很多麻烦。

这种方法的问题根源在于 Scala 会根据需要自动注入(inject)隐式转换以使方法调用正常工作。这可能会产生一些非常不幸的后果。例如:

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<String, Int> 的隐式转换,它很高兴地接受了新值并立即被丢弃。原文map是未修改的(因为它确实是不可变的)。

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

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

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

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

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

关于Java <-> Scala 互操作 : transparent List and Map conversion,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1519838/

相关文章:

c# - 从 C++ 调用 C# 代码,但 ExecuteInDefaultAppDomain() 太有限

java - jQuery/JSON 调用的 REST WS 没有返回响应

java - 代码可以编译但不能运行

java - 尝试返回自定义对象类型时出现 SerializationException

scala - 从哪里开始依赖类型编程?

c# - 使用 Interop 在 C# 中创建 Word 文档的更快方法

java - Xamarin:在 Visual Studio 2013 中更改 JDK 位置

scala - 提升 Web 框架 DRY 调度

scala - 如何避免使用 scala wart 推断类型不包含任何内容?

c# - 是否可以使用 C# 与 Thunderbird 互操作?