2009-10-05 6 views
33

私はScalaを学んでおり、私はScalaに移行するためのJavaプロジェクトを持っています。クラスを1つずつ書き直して、新しいクラスがプロジェクトを中断していないことを確認して、そのクラスを移行したいと考えています。Java <-> Scala interop:transparentリストとマップの変換

このJavaプロジェクトでは、java.util.Listjava.util.Mapのロットが使用されています。新しいScalaクラスでは、ScalaのListMapを使用して、見栄えの良いScalaコードを作成したいと考えています。

問題は、新しいクラス(Scalaで使用されるクラス)が、既存のJavaコードとシームレスに統合されないという点です。Javaにはjava.util.Listが必要であり、Scalaには独自のscala.Listが必要です。

ここでは、問題の簡単な例を示します。クラスがありますメイン,ロジック,ダオです。彼らはラインでお互いを呼び出します:メイン - >ロジック - >ダオ。私の状況で

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); 
    } 
} 

、クラスメインダオは、フレームワーククラス(私はそれらを移行する必要はありません)です。クラス論理はビジネスロジックであり、Scalaのクールな機能から多くの利益を得ます。

は、私はクラスメインダオとの整合性を維持しながら、Scalaでクラスロジックを書き換える必要があります。最高の書き換えは(動作しない)ようになります。

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.util.Listとフレンドを使用するJava - > Scalaコードを移行するためのベストプラクティスは何ですか?

答えて

65

私を信頼してください。あなたはを望んでいません透明な前後に変換します。これはまさにscala.collection.jcl.Conversions機能がしようとしているものです。実際には、頭痛の原因になります。

このアプローチの問題の根本は、メソッド呼び出しを行うために必要なときに自動的に暗黙の変換を挿入することです。これは本当に不幸な結果をもたらすことがあります。たとえば:

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は変更されていません(実際は不変です)。

  • 追加メンバー(メソッド、フィールドなど):彼はあなたが2つのだけの目的のいずれかの暗黙の型変換を定義する必要があることを言うとき

    ホルヘ・オルティスは最高のそれを置きます。これらの変換は、スコープ内の他のものとは関係のない新しいタイプにする必要があります。

  • 壊れたクラス階層を「修正」します。したがって、あなたはいくつかのタイプがある場合ABは無関係です。 A <: B<:は「サブタイプ」を意味する)を希望する場合は、変換A => Bのみをと定義することができます。

java.util.Mapは明らかに、階層内のものとは無関係の新しいタイプではないため、最初の条件に該当しません。したがって、私たちの唯一の希望は、Map[A, B] => java.util.Map[A, B]の変換が2番目の変換に適格であることです。しかし、ScalaのMapjava.util.Mapから継承することは絶対に意味がありません。それらは本当に完全に直交するインターフェース/特性です。上に示したように、これらのガイドラインを無視しようとすると、ほとんど常に奇妙な予期しない動作が発生します。

実際、javautils とasJavaの方法は、この正確な問題を解決するために設計されています。 javautilsに暗黙の変換(実際には数多く)がMap[A, B] => RichMap[A, B]からです。 RichMapは、javautilsによって定義された全く新しいタイプです。その唯一の目的は、Mapにメンバーを追加することです。特にasJavaメソッドを追加し、java.util.Mapを実装し、元のMapインスタンスに代理人を実装するラッパーマップを返します。これにより、プロセスははるかに明示的になり、エラーが発生しにくくなります。

つまり、とasJavaを使用すると、がベストプラクティスです。本番環境ではこれらの両方の道路を独立して立ち上げたので、私はjavautilsのアプローチがもっと安全で簡単に行えることを初めて教えてください。あなた自身の8文字を節約するために、保護を迂回しようとしないでください!ここで

+1

非常によく置いて、+1 – geowa4

+1

(+1)は非常にきれいに述べた。私はこのポストがいつか戻って苦労していたときに、このポストがあったことを願っています。 – Shaun

+1

このような徹底的な書面をありがとう。 Googleでこれを見つけました(もちろん!)すでに誰かに苦痛をたくさん救った! –

3

はホルヘ・オルティスの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プロジェクトは、Scalaの2.8でcentral maven repository

2

から入手可能です、それはこのように行うことができる:

import scala.collection.JavaConversions._ 

val list = new java.util.ArrayList[String]() 
list.add("test") 
val scalaList = list.toList 
関連する問題