33

私はScalaを学んでおり、Scalaに移行するためのJavaプロジェクトがあります。クラスを1つずつ書き直し、新しいクラスがプロジェクトを壊さないことを確認して、移行したいと思います。

このJavaプロジェクトは多くのjava.util.Listとを使用しますjava.util.MapList新しいScalaクラスでは、Scalaを使用し、Map見栄えの良いScalaコードを使用したいと思います。

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

これが問題の簡単な例です。MainLogicDaoのクラスがあります。彼らは一列にお互いを呼びます:メイン->ロジック->ダオ

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はフレームワーククラスです(これらを移行する必要はありません)。クラスロジックはビジネスロジックであり、Scalaのクールな機能から多くの恩恵を受けるでしょう。

MainクラスとDaoクラスとの整合性を維持しながら、ScalaでLogicクラスを書き直す必要があります。最良の書き換えは次のようになります(機能しません):

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

4

3 に答える 3

65

私を信じて; 透過的な変換を行ったり来たりする必要はありませんこれはまさに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コレクションフレームワークや不変コレクションの概念に不慣れな人にとっては完全に性格が悪いわけではありません。残念ながら、それは完全に間違っています。この関数の結果は同じマップです。toを呼び出すと、へputの暗黙的な変換がトリガーされますjava.util.Map<String, Int>。これは、新しい値を受け入れ、すぐに破棄されます。オリジナルmapは変更されていません(実際、不変です)。

Jorge Ortizは、次の2つの目的のいずれかのために暗黙の変換のみを定義する必要があると彼が言ったときにそれを最もよく言います。

  • メンバー(メソッド、フィールドなど)を追加します。これらの変換は、スコープ内の他のものとは関係のない新しいタイプに変換する必要があります。
  • 壊れたクラス階層を「修正」します。したがって、いくつかのタイプがAあり、Bそれらが無関係である場合。変換を定義できるのは、必要なA => B場合に限りますA <: B<:「サブタイプ」を意味します)。

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

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

つまり、とを使用asScalaすることasJava ベストプラクティスです。本番アプリケーションでこれらの両方の道を独立して進んだので、javautilsアプローチの方がはるかに安全で、操作が簡単であることが直接わかります。自分自身を8文字節約するためだけに、その保護を回避しようとしないでください。

于 2009-10-05T13:47:30.457 に答える
3

JorgeOrtizの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リポジトリから入手できます。

于 2009-11-12T04:55:19.840 に答える
2

Scala 2.8では、次のように実行できます。

import scala.collection.JavaConversions._

val list = new java.util.ArrayList[String]()
list.add("test")
val scalaList = list.toList
于 2012-05-28T18:04:48.720 に答える