10

私は、既存のJavaライブラリー用の暗黙のScalaラッパー・クラスのセットを作成しています (そのライブラリーを装飾して、 Scala開発者にとってより便利なものにすることができます)。

簡単な例として、Javaライブラリ (私は変更できません) に次のようなクラスがあるとします。

public class Value<T> {
    // Etc.
    public void setValue(T newValue) {...}
    public T getValue() {...}
}

ここで、このクラスをScalaスタイルのゲッターとセッターで装飾したいとしましょう。これは、次の暗黙のクラスで行うことができます。

final implicit class RichValue[T](private val v: Value[T])
extends AnyVal {
  // Etc.
  def value: T = v.getValue
  def value_=(newValue: T): Unit = v.setValue(newValue)
}

このimplicitキーワードは、ScalaValueコンパイラーに、 のインスタンスを のインスタンスにRichValue暗黙的に変換できることを伝えます (後者がスコープ内にある場合)。これで、内で定義されたメソッドRichValueを のインスタンスに適用できるようになりValueました。例えば:

def increment(v: Value[Int]): Unit = {
  v.value = v.value + 1
}

(これはあまり良いコードではなく、正確に機能するわけでもありません。単純な使用例を示しているだけです。)

残念ながら、Scalaimplicitではクラスをトップレベルにすることは許可されていないためpackage object、. (この制限が必要な理由はわかりませんが、暗黙の変換関数との互換性のためだと思います。)objectclasstraitpackage

ただし、これを値クラスRichValueにするためにからも拡張しています。それらに慣れていない場合は、Scalaコンパイラーが割り当ての最適化を行うことができます。特に、コンパイラは常に のインスタンスを作成する必要はなく、値クラスのコンストラクタ引数を直接操作できます。AnyValRichValue

言い換えれば、Scala の暗黙的な値クラスをラッパーとして使用することによるパフォーマンスのオーバーヘッドはほとんどなく、これは素晴らしいことです。:-)

ただし、値クラスの主な制限は、classまたはtrait;内で定義できないことです。packages、package objects、またはs のメンバーにしかなれませんobject。(これは、外部クラス インスタンスへのポインタを維持する必要がないようにするためです。)

暗黙の値クラスは両方の制約セットを尊重する必要があるため、package objectまたは内でのみ定義できますobject

そしてそこに問題があります。私がラップしているライブラリには、膨大な数のクラスとインターフェースを持つパッケージの深い階層が含まれています。import理想的には、次のような単一のステートメントで ラッパー クラスをインポートできるようにしたいと考えています。

import mylib.implicits._

それらをできるだけ簡単に使用できるようにします。

これを達成するために私が現在確認できる唯一の方法は、すべての暗黙的な値クラス定義を 1つのソース ファイル内の 1 つのpackage object(または) 内に配置することです。object

package mylib
package object implicits {

  implicit final class RichValue[T](private val v: Value[T])
  extends AnyVal {
    // ...
  }

  // Etc. with hundreds of other such classes.
}

importただし、これは理想とはほど遠いものであり、ターゲット ライブラリのパッケージ構造をミラーリングしながら、1 つのステートメントですべてをスコープに含めたいと考えています。

このアプローチの利点を犠牲にすることなく、これを達成する簡単な方法はありますか?

(たとえば、これらのラッパーを値クラスtraitにするのをやめると、コンポーネント パッケージごとに 1 つずつ、多数の異なる s 内でそれらを定義し、ルートpackage objectにそれらすべてを拡張させて、単一のインポートしますが、利便性のためにパフォーマンスを犠牲にしたくありません。)

4

1 に答える 1