サーチ…


構文

  • 暗黙のval x:T = ???

備考

暗黙のクラスでは、既存の型にカスタムメソッドを追加することができ、コードを変更することなくコードを制御することなく型を充実させることができます。

暗黙の型を使用して既存のクラスを充実させることは、しばしば「ライブラリを充実させる」パターンと呼ばれます。

暗黙的なクラスの制限

  1. 暗黙のクラスは、別のクラス、オブジェクト、または特性内にのみ存在することがあります。
  2. 暗黙のクラスは、暗黙的でない1次コンストラクタ・パラメータを1つだけ持つことができます。
  3. 暗黙のクラスと同じ名前を持つ同じスコープ内に別のオブジェクト、クラス、特性、またはクラスメンバー定義が存在しないことがあります。

暗黙の変換

暗黙的な変換により、あるタイプのオブジェクトを別のタイプに自動的に変換することができます。これにより、コードはオブジェクトを別の型のオブジェクトとして扱うことができます。

case class Foo(i: Int)

// without the implicit
Foo(40) + 2    // compilation-error (type mismatch)

// defines how to turn a Foo into an Int
implicit def fooToInt(foo: Foo): Int = foo.i

// now the Foo is converted to Int automatically when needed
Foo(40) + 2    // 42

変換は一方向です:この場合、 42Foo(42)戻すことはできません。これを行うには、2番目の暗黙の変換を定義する必要があります。

implicit def intToFoo(i: Int): Foo = Foo(i)

これは、float値を整数値などに追加できるメカニズムであることに注意してください。

暗黙のコンバージョンは、何が起こっているのかを難読化するため、控えめに使用する必要があります。暗黙の変換を使用することによる有形の可読性利得がない限り、メソッド呼び出しによる明示的な変換を使用することがベストプラクティスです。

暗黙的な変換にはパフォーマンスに大きな影響はありません。

Scalaは自動的に、JavaからScalaへのすべての変換を含む、 scala.Predefさまざまな暗黙の変換をscala.Predefします。これらは、デフォルトではファイルのコンパイルに含まれます。

暗黙のパラメータ

暗黙のパラメータは、型のパラメータをスコープ内で一度定義し、その型の値を使用するすべての関数に適用する必要がある場合に便利です。

通常の関数呼び出しは、次のようになります。

// import the duration methods
import scala.concurrent.duration._

// a normal method:
def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis

val timeout = 1.second
// timeout: scala.concurrent.duration.FiniteDuration = 1 second

// to call it
doLongRunningTask(timeout) // 1000

ここでは、すべてのタイムアウト時間があるいくつかのメソッドがあり、同じタイムアウトを使用してこれらのメソッドをすべて呼び出すとします。暗黙の変数としてタイムアウトを定義できます。

// import the duration methods
import scala.concurrent.duration._

// dummy methods that use the implicit parameter
def doLongRunningTaskA()(implicit timeout: FiniteDuration): Long = timeout.toMillis
def doLongRunningTaskB()(implicit timeout: FiniteDuration): Long = timeout.toMillis

// we define the value timeout as implicit
implicit val timeout: FiniteDuration = 1.second

// we can now call the functions without passing the timeout parameter
doLongRunningTaskA() // 1000
doLongRunningTaskB() // 1000

これが動作する方法は、scalacコンパイラが暗黙的にマークされスコープ内の値を探し、 その型が暗黙のパラメータの型と一致することです。それが見つかった場合は、それを暗黙のパラメーターとして適用します。

スコープ内で同じタイプの2つ以上の暗黙の定義を定義すると、これは機能しません。

エラーメッセージをカスタマイズするには、型にimplicitNotFoundアノテーションを使用します。

@annotation.implicitNotFound(msg = "Select the proper implicit value for type M[${A}]!")
case class M[A](v: A) {}

def usage[O](implicit x: M[O]): O = x.v

//Does not work because no implicit value is present for type `M[Int]`
//usage[Int]   //Select the proper implicit value for type M[Int]!
implicit val first: M[Int] = M(1)
usage[Int]     //Works when `second` is not in scope
implicit val second: M[Int] = M(2)
//Does not work because more than one implicit values are present for the type `M[Int]`
//usage[Int]   //Select the proper implicit value for type M[Int]!

タイムアウトは通常のユースケースです。たとえばAkkaの場合 、ActorSystemは常に(ほとんどの場合は)同じであるため、通常は暗黙的に渡されます。別のユースケースは、(のような型クラスに依存しているFPライブラリで最も一般的に、ライブラリーの設計になりscalaz歓喜 )。

IntLongStringなどの基本型で暗黙的なパラメータを使用することは、一般に悪い習慣とみなされます。混乱を招き、コードを読みにくくするからです。

暗黙のクラス

暗黙のクラスは、以前に定義されたクラスに新しいメソッドを追加することを可能にします。

StringクラスにはwithoutVowelsメソッドはありません。これは次のように追加できます:

object StringUtil {
  implicit class StringEnhancer(str: String) {
    def withoutVowels: String = str.replaceAll("[aeiou]", "")
  }
}

暗黙のクラスには、拡張したい型( String )を持つ単一のコンストラクタパラメータ( str )があり、型( withoutVowels )に "追加"するメソッドが含まれています。新しく定義されたメソッドは、エンハンスドタイプ(エンハンスタイプが暗黙的スコープの場合)で直接使用できるようになりました。

import StringUtil.StringEnhancer // Brings StringEnhancer into implicit scope

println("Hello world".withoutVowels) // Hll wrld

内部では、暗黙のクラスは、次のように、拡張された型から暗黙のクラスへの暗黙の変換を定義します。

implicit def toStringEnhancer(str: String): StringEnhancer = new StringEnhancer(str)

暗黙のクラスは、ランタイムオブジェクトの作成を避け、ランタイムオーバーヘッドを削除するために、 Valueクラスとして定義されることがよくあります。

implicit class StringEnhancer(val str: String) extends AnyVal {
    /* conversions code here */
}

上記の改良された定義では、 StringEnhancer新しいインスタンスは、 withoutVowelsメソッドが呼び出されるたびに作成する必要はありません。

'暗黙的に'暗黙のパラメータを使用して解決する

複数の暗黙のパラメータを持つ暗黙のパラメータリストを仮定します。

case class Example(p1:String, p2:String)(implicit ctx1:SomeCtx1, ctx2:SomeCtx2)

ここで、暗黙のインスタンスの1つが利用可能でない( SomeCtx1 )、他の暗黙のインスタンスがすべてスコープ内にあるとSomeCtx1すると、クラスのインスタンスを作成するにはSomeCtx1インスタンスを提供する必要があります。

これはimplicitlyキーワードを使用してスコープ内の暗黙のインスタンスを相互に保持しながら行うことができます:

Example("something","somethingElse")(new SomeCtx1(), implicitly[SomeCtx2])

REPLのインプリシット

REPLセッション中にスコープ内のimplicitsをすべて表示するには、次のようにします。

scala> :implicits

Predef.scala定義された暗黙的な変換も含めるには:

scala> :implicits -v

式があり、それに適用されるすべての書き換えルールの効果を表示したい場合は(暗黙を含む):

scala> reflect.runtime.universe.reify(expr) // No quotes. reify is a macro operating directly on code.

(例:

scala> import reflect.runtime.universe._
scala> reify(Array("Alice", "Bob", "Eve").mkString(", "))
resX: Expr[String] = Expr[String](Predef.refArrayOps(Array.apply("Alice", "Bob", "Eve")(Predef.implicitly)).mkString(", "))



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow