簡體   English   中英

Java < - > Scala interop:透明的List和Map轉換

[英]Java <-> Scala interop: transparent List and Map conversion

我正在學習Scala,我有一個Java項目要遷移到Scala。 我想通過逐個重寫類並檢查新類沒有破壞項目來遷移它。

這個Java項目使用了很多java.util.Listjava.util.Map 在新的Scala類中,我想使用Scala的ListMap來獲得漂亮的Scala代碼。

問題是新類(那些在Scala中是wtitten)不會無縫地與現有的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類是框架類(我不需要遷移它們)。 Class Logic是業務邏輯,將從Scala cool功能中獲益良多。

我需要在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-javautilsGitHub )):

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)?

如果不可能,遷移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未經修改(因為它確實是不可變的)。

Jorge Ortiz說得最好,他說你應該只為兩個目的之一定義隱式轉換:

  • 添加成員(方法,字段等)。 這些轉換應該是范圍內任何其他內容無關的新類型。
  • “修復”一個破碎的類層次結構。 因此,如果你有一些不相關的類型AB 當且當您希望A <: B<:表示“子類型”)時,您才可以定義轉換A => B

由於java.util.Map顯然不是與我們的層次結構中的任何內容無關的新類型,因此我們不能歸入第一個附帶條件。 因此,我們唯一的希望是我們的轉換Map[A, B] => java.util.Map[A, B]有資格獲得第二個。 但是,Scala的Mapjava.util.Map繼承是完全沒有意義的。 它們實際上是完全正交的接口/特征。 如上所述,試圖忽略這些指導原則幾乎總會導致奇怪和意外的行為。

事實上,javautils asScalaasJava方法旨在解決這個確切的問題。 Map[A, B] => RichMap[A, B]有一個隱式轉換(實際上是其中一些)。 RichMapRichMap定義的全新類型,因此它的唯一目的是向Map添加成員。 特別是,它添加了asJava方法,該方法返回一個包裝器映射,該映射實現java.util.Map並委托給原始Map實例。 這使得該過程更加明確,並且更不容易出錯。

換句話說,使用asScalaasJava 最佳實踐。 在生產應用程序中獨立地沿着這兩條道路走下去,我可以直接告訴你javautils方法更安全,更容易使用。 不要僅僅為了節省自己8個字符而試圖繞過它的保護!

以下是使用Jorge Ortiz的scalaj-collection庫的一些簡單示例:

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存儲庫獲得

使用Scala 2.8,可以這樣做:

import scala.collection.JavaConversions._

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM