16

Scala 2.9 では、カスタム メソッドをライブラリ クラスに追加する (エンリッチまたは "pimp") には、次のように記述する必要がありました。

object StringPimper {
  implicit def pimpString(s: String) = new {
    def greet: String = "Hello " + s
  }
}

Scala 2.10 がリリースされたとき、匿名クラスのオブジェクトを返す暗黙的なメソッドの必要性を取り除くことで、理論的には上記のタスクを単純化することを意図した暗黙的なクラス定義が導入されたことを読んだことがあります。私はそれが私がただ書くことを可能にするだろうと思った

implicit class PimpedString(s: String) {
  def greet: String = "Hello " + s
}

これは私にとってはるかにきれいに見えます。ただし、そのような定義はコンパイル エラーを引き起こします。

`implicit' modifier cannot be used for top-level objects

これは、コードをオブジェクトに再度ラップすることで解決されます。

object StringPimper {
  implicit class PimpedString(s: String) {
    def greet: String = "Hello " + s
  }
}

言うまでもなく、これは改善の感覚をほとんど中和します。

では、短く書く方法はありますか?ラッパー オブジェクトを取り除くには?

私は実際MyApp.pimpsにすべての売春斡旋業者が行くパッケージを持っており(あまり多くはありません。持っていれば別のパッケージを使用します)、orのMyApp.pimps.StringPimper._代わりにインポートするのにうんざりしています。もちろん、すべての暗黙的なクラスを 1 つのラッパー オブジェクトに入れることもできますが、これはそれらすべてを 1 つのファイルに入れることを意味し、かなり長くなり、非常に醜い解決策になります。MyApp.pimps.PimpedStringMyApp.pimps._

4

4 に答える 4

10

標準的な用語は今エンリッチです。それを使用するライブラリ メソッドが not のような名前であったことを考えると、なぜそれが以前になかったのかはわかりませRichStringPimpedString

とにかく、暗黙のクラスを何も配置することはできませんが、暗黙を使用するにはメソッドが必要であり、メソッドを何も配置することはできません。しかし、不正行為を行ってすべてを取得することはできますpimps

// This can go in one file
trait ImplicitsStartingWithB {
  implicit class MyBoolean(val bool: Boolean) { def foo = !bool }
  implicit class MyByte(val byte: Byte) { def bar = byte*2 }
}

// This can go in another file
trait ImplicitsStartingWithS {
  implicit class MyShort(val short: Short) { def baz = -short }
  implicit class MyString(val st: String) { def bippy = st.reverse }
}

// This is a third file, if you want!
object AllImplicits extends ImplicitsStartingWithB with ImplicitsStartingWithS {}

scala> import AllImplicits._
import AllImplicits._

scala> true.foo
res0: Boolean = false

これはパッケージ オブジェクトでも実行できpimpsます。通常はpackage.scala、 というディレクトリ内にというファイルを配置してpimpsから、

package object pimps extends /* blah blah ... */ {
  /* more stuff here, if you need it */
}

1 つの注意点は、新しい機能としての値クラスにはかなりの数の制限があることです。不要なものもありますが、新しいクラスの割り当てを回避したい場合MyBoolean(JVM は多くの場合大幅に最適化できますが、通常はメソッド呼び出しほどではありません)、制限を回避する必要があります。その場合、

// In some file, doesn't really matter where
class MyBoolean(val bool: Boolean) extends AnyVal { def foo = !bool }

package object pimps {
  def implicit EnrichWithMyBoolean(b: Boolean) = new MyBoolean(b)
}

これは作業を節約しません (ただし、実行速度は速くなります)。

于 2013-02-10T21:27:47.477 に答える
5

エンリッチメント クラスを別のファイルに配置できるようにするための回避策は、オブジェクトではなくトレイトでラップすることです。

// file: package.scala
package object mypackage extends StringImplicits {
  def test { println("Scala".greet) }
}

// file: StringImplicits.scala
package mypackage

trait StringImplicits {
  implicit class RichString(s: String) {
    def greet: String = "Hello " + s
  }
}

ただし、Rex が指摘するように、値クラスを他のクラス内で使用することはできません。したがって、Scala 2.10 の値クラスを利用したい場合は、ネストされた暗黙のクラスを使用できません。ああ!

于 2013-02-10T21:30:31.423 に答える
3

まあ、コンパイラエラーはそれをすべて言います。トップレベルで暗黙のクラスを定義することはできません。つまり、ラッパー オブジェクト (パッケージ オブジェクトの場合もあります) を使用するか、スコープ内でクラスを直接定義するかのいずれかを意味します (再利用可能である必要がないまれなケースで)。

その理由は、ラッパーなしでは暗黙的な変換をインポートできないためです。暗黙のクラス (正確には変換) を独自の名前でインポートすることはできません。

それに加えて、少し異なる署名を使用することをお勧めします ( Value Classesを参照)。

implicit class PimpedString(val self: String) extends AnyVal

これには、拡張メソッドの呼び出しをインライン化できる (そしてインライン化する) という効果があります。

于 2013-02-10T21:04:00.937 に答える
2

暗黙の宣言を配置するもう 1 つの非常に良い場所は、コンパニオン オブジェクトです。

Type1からType2Scalaへの暗黙的な変換では、両方の型のすべての祖先のコンパニオン オブジェクトを調べます。そのため、明示的なインポートを必要としない場所に暗黙的な変換を配置するのは簡単です。

case class Curie(curie:String)

object Curie {
  implicit def toCurie(s:String) = Curie(curie)
}

変換メソッドを必要Curieとするメソッドを呼び出すと、String追加のインポートなしで呼び出されます。

于 2013-09-07T17:51:19.640 に答える