![](/img/trans.png)
[英]Java LinkedHashMap to scala Map conversion and recursive conversion
[英]Java <-> Scala interop: transparent List and Map conversion
我正在學習Scala,我有一個Java項目要遷移到Scala。 我想通過逐個重寫類並檢查新類沒有破壞項目來遷移它。
這個Java項目使用了很多java.util.List
和java.util.Map
。 在新的Scala類中,我想使用Scala的List
和Map
來獲得漂亮的Scala代碼。
問題是新類(那些在Scala中是wtitten)不會無縫地與現有的Java代碼集成:Java需要java.util.List
,Scala需要它自己的scala.List
。
以下是該問題的簡化示例。 有Main , Logic , Dao類 。 他們互相稱呼: 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類是框架類(我不需要遷移它們)。 Class Logic是業務邏輯,將從Scala cool功能中獲益良多。
我需要在Scala中重寫類Logic ,同時保持Main和Dao類的完整性。 最好的重寫看起來像(不起作用):
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之間的列表和映射的透明魔術轉換(無需執行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說得最好,他說你應該只為兩個目的之一定義隱式轉換:
A
和B
當且僅當您希望A <: B
( <:
表示“子類型”)時,您才可以定義轉換A => B
由於java.util.Map
顯然不是與我們的層次結構中的任何內容無關的新類型,因此我們不能歸入第一個附帶條件。 因此,我們唯一的希望是我們的轉換Map[A, B] => java.util.Map[A, B]
有資格獲得第二個。 但是,Scala的Map
從java.util.Map
繼承是完全沒有意義的。 它們實際上是完全正交的接口/特征。 如上所述,試圖忽略這些指導原則幾乎總會導致奇怪和意外的行為。
事實上,javautils asScala
和asJava
方法旨在解決這個確切的問題。 在Map[A, B] => RichMap[A, B]
有一個隱式轉換(實際上是其中一些)。 RichMap
是RichMap
定義的全新類型,因此它的唯一目的是向Map
添加成員。 特別是,它添加了asJava
方法,該方法返回一個包裝器映射,該映射實現java.util.Map
並委托給原始Map
實例。 這使得該過程更加明確,並且更不容易出錯。
換句話說,使用asScala
和asJava
是最佳實踐。 在生產應用程序中獨立地沿着這兩條道路走下去,我可以直接告訴你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.