Java< - > Scala interop:透明列表和地图转换 [英] Java <-> Scala interop: transparent List and Map conversion

查看:145
本文介绍了Java< - > Scala interop:透明列表和地图转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am learning Scala and I have a Java project to migrate to Scala. I want to migrate it by rewriting classes one-by-one and checking that new class didn't break the project.

这个Java项目使用了大量的 java.util.List java.util.Map 。在新的Scala类中,我想使用Scala的 List Map 来获得漂亮的Scala代码。

This Java project uses lots of java.util.List and java.util.Map. In new Scala classes I would like to use Scala’s List and Map to have good-looking Scala code.

问题是新类(Scala中的wtitten)没有与现有Java代码无缝集成:Java需要 java.util.List ,Scala需要自己的 scala.List

The problem is that new classes (those are wtitten in Scala) do not integrate seamelessly with existing Java code: Java needs java.util.List, Scala needs its own scala.List.

以下是问题的简化示例。有类 Main Logic Dao 。他们互相称呼: Main - > Logic - > Dao

Here is a simplified example of the problem. There are classes Main, Logic, Dao. They call each other in a line: 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);
    }
}

在我的情况下,班级 Main Dao 是框架类(我不需要迁移它们)。类逻辑是业务逻辑,将从Scala酷功能中获益良多。

In my situation, classes Main and Dao are framework classes (I don’t need to migrate them). Class Logic is business-logic and will benefit a lot from Scala cool features.

我需要重写类逻辑在Scala中使用类 Main Dao 保持完整性。最好的重写看起来像(不起作用):

I need to rewrite class Logic in Scala while preserving integrity with classes Main and Dao. The best rewrite would look like (doesn’t work):

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

理想行为: Logic2 中的列表是本机Scala列表。全部输入/输出 java.util.Lists 自动装箱/取消装箱。但这不起作用。

Ideal behaviour: Lists inside Logic2 are native Scala Lists. All in/out java.util.Lists get boxed/unboxed automagically. But this doesn't work.

相反,这确实有效(感谢 scala-javautils GitHub )):

Instead, this does work (thanks to 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之间实现列表和地图的透明魔术转换(不需要执行toScala / toJava)?

How do I achieve transparent magic conversion of Lists and Maps between Java <-> Scala (without need to do toScala/toJava)?

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

If it is not possible, what are the best practices for migrating Java -> Scala code that uses java.util.List and friends?

推荐答案

相信我;你没有希望来回透明转换。这正是 scala.collection.jcl.Conversions 函数试图做的事情。在实践中,它会引起很多麻烦。

Trust me; you don't want transparent conversion back and forth. This is precisely what the scala.collection.jcl.Conversions functions attempted to do. In practice, it causes a lot of headaches.

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

The root of the problem with this approach is Scala will automatically inject implicit conversions as necessary to make a method call work. This can have some really unfortunate consequences. For example:

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> ,它很乐意接受新的价值并立即被丢弃。原始的地图未经修改(因为它确实是不可变的)。

This code wouldn't be entirely out of character for someone who is new to the Scala collections framework or even just the concept of immutable collections. Unfortunately, it is completely wrong. The result of this function is the same map. The call to put triggers an implicit conversion to java.util.Map<String, Int>, which happily accepts the new values and is promptly discarded. The original map is unmodified (as it is, indeed, immutable).

Jorge Ortiz说得最好说你应该只为两个目的之一定义隐式转换:

Jorge Ortiz puts it best when he says that you should only define implicit conversions for one of two purposes:


  • 添加成员(方法,字段等)。这些转换应该是与无关的新类型到范围内的任何其他内容。

  • 修复损坏的类层次结构。因此,如果你有一些 A B 这些不相关的类型。您可以定义转化 A => B if和如果您希望 A<:B <:表示子类型)。

  • Adding members (methods, fields, etc). These conversions should be to a new type unrelated to anything else in scope.
  • "Fixing" a broken class hierarchy. Thus, if you have some types A and B which are unrelated. You may define a conversion A => B if and only if you would have preferred to have A <: B (<: means "subtype").

因为 java.util。地图显然不是与我们的层次结构中的任何内容无关的新类型,我们不能归入第一个附带条件。因此,我们唯一的希望是我们的转换地图[A,B] => java.util.Map [A​​,B] 有资格获得第二个。但是,Scala的 Map 继承自 java.util.Map 完全没有意义。它们实际上是完全正交的接口/特征。如上所述,试图忽略这些指导方针几乎总会导致奇怪和意外的行为。

Since java.util.Map is obviously not a new type unrelated to anything in our hierarchy, we can't fall under the first proviso. Thus, our only hope is for our conversion Map[A, B] => java.util.Map[A, B] to qualify for the second one. However, it makes absolutely no sense for Scala's Map to inherit from java.util.Map. They are really completely orthogonal interfaces/traits. As demonstrated above, attempting to ignore these guidelines will almost always result in weird and unexpected behavior.

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

The truth is that the javautils asScala and asJava methods were designed to solve this exact problem. There is an implicit conversion (a number of them actually) in javautils from Map[A, B] => RichMap[A, B]. RichMap is a brand new type defined by javautils, so its only purpose is to add members to Map. In particular, it adds the asJava method, which returns a wrapper map which implements java.util.Map and delegates to your original Map instance. This makes the process much more explicit and far less error prone.

换句话说,使用 asScala asJava 的最佳做法。在生产应用程序中独立地沿着这两条道路走下去,我可以直接告诉你javautils方法更安全,更容易使用。不要仅仅为了节省自己8个字符而试图绕过它的保护!

In other words, using asScala and asJava is the best practice. Having gone down both of these roads independently in a production application, I can tell you first-hand that the javautils approach is much safer and easier to work with. Don't try to circumvent its protections merely for the sake of saving yourself 8 characters!

这篇关于Java&lt; - &gt; Scala interop:透明列表和地图转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆