329

私はScalaのプレイフレームワークのチュートリアルを進めていましたが、このコードスニペットに出くわしました。

def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
        errors => BadRequest(views.html.index(Task.all(), errors)),
        label => {
          Task.create(label)
          Redirect(routes.Application.tasks())
        } 
  )
}

それで私は調査することに決め、この投稿に出くわしました。

まだわかりません。

これの違いは何ですか:

implicit def double2Int(d : Double) : Int = d.toInt

def double2IntNonImplicit(d : Double) : Int = d.toInt

明らかな事実を除いて、それらは異なるメソッド名を持っています。

いつ、なぜ使用する必要がありますimplicitか?

4

7 に答える 7

428

暗黙の主なユースケースを以下で説明しますが、詳細については、Scalaでのプログラミングの関連する章を参照してください。

暗黙のパラメータ

メソッドの最後のパラメーターリストにマークを付けることができますimplicit。これは、値が呼び出されたコンテキストから取得されることを意味します。スコープに正しいタイプの暗黙の値がない場合、コンパイルされません。暗黙の値は単一の値に解決する必要があり、衝突を避けるために、その目的に固有の型を作成することをお勧めします。たとえば、暗黙の値を見つけるためにメソッドを必要としないでくださいInt

例:

  // probably in a library
class Prefixer(val prefix: String)
def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

  // then probably in your application
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc")  // returns "***abc"

暗黙の変換

Functionコンパイラーは、コンテキストに対して間違った型の式を見つけると、型チェックを可能にする型の暗黙の値を探します。したがって、Aが必要で、が見つかった場合、スコープ内Bでタイプの暗黙的な値を探します(オブジェクトやコンパニオンオブジェクトが存在する場合はB => A、他の場所もチェックします)。sはオブジェクトに「eta-expanded」できるので、anも同様に機能します。BAdefFunctionimplicit def xyz(arg: B): A

したがって、メソッドの違いは、 aが見つかったが、が必須であるimplicit場合に、マークされたメソッドがコンパイラによって挿入されることです。DoubleInt

implicit def doubleToInt(d: Double) = d.toInt
val x: Int = 42.0

と同じように動作します

def doubleToInt(d: Double) = d.toInt
val x: Int = doubleToInt(42.0)

2番目では、変換を手動で挿入しました。最初に、コンパイラは同じことを自動的に行いました。左側に型注釈があるため、変換が必要です。


Playの最初のスニペットについて:

このページでは、Playのドキュメントからアクションについて説明しています( APIのドキュメントも参照してください)。使用しています

apply(block: (Request[AnyContent]) ⇒ Result): Action[AnyContent]

Actionオブジェクト(同じ名前のトレイトのコンパニオン)。

したがって、引数として関数を指定する必要があります。これは、次の形式のリテラルとして記述できます。

request => ...

関数リテラルでは、の前の部分=>は値宣言であり、implicit他のval宣言と同じように、必要に応じてマークを付けることができます。ここでは、タイプチェックのためにこれをマークする必要はありrequest ませんが、そうすることで、関数内でそれを必要とする可能性のあるすべてのメソッドの暗黙的な値として使用できるようになります(もちろん、明示的に使用することもできます) 。この特定のケースでは、 Formクラスのメソッドが暗黙の引数を必要とするため、これが行われました。implicitbindFromRequestRequest

于 2012-04-29T21:02:02.927 に答える
48

警告:皮肉が慎重に含まれています!YMMV..。

ルイージの答えは完全で正しいです。これは、Scalaプロジェクトで非常に頻繁に発生するように、暗黙的要素を華麗に使いすぎる方法の例を少し拡張するだけです。実際、非常に頻繁に、 「ベストプラクティス」ガイドの1つでそれを見つけることさえできるでしょう。

object HelloWorld {
  case class Text(content: String)
  case class Prefix(text: String)

  implicit def String2Text(content: String)(implicit prefix: Prefix) = {
    Text(prefix.text + " " + content)
  }

  def printText(text: Text): Unit = {
    println(text.content)
  }

  def main(args: Array[String]): Unit = {
    printText("World!")
  }

  // Best to hide this line somewhere below a pile of completely unrelated code.
  // Better yet, import its package from another distant place.
  implicit val prefixLOL = Prefix("Hello")
}
于 2017-01-18T00:01:38.663 に答える
27

Scalaでは暗黙的に次のように機能します:

コンバータ

パラメータ値インジェクター

拡張方法

Implicitにはいくつかの用途があります

  1. 暗黙的な型変換:エラーを生成する割り当てを目的の型に変換します

    val x :String = "1"
    
    val y:Int = x
    

文字列はIntのサブタイプではないため、2行目でエラーが発生します。エラーを解決するために、コンパイラはスコープ内で暗黙のキーワードを持ち、引数として文字列を取り、 Intを返すそのようなメソッドを探します。

それで

implicit def z(a:String):Int = 2

val x :String = "1"

val y:Int = x // compiler will use z here like val y:Int=z(x)

println(y) // result 2  & no error!
  1. 暗黙的な受信者変換:通常、受信者はオブジェクトのプロパティを呼び出します。メソッドまたは変数。したがって、レシーバーによってプロパティを呼び出すには、プロパティがそのレシーバーのクラス/オブジェクトのメンバーである必要があります。

     class Mahadi{
    
     val haveCar:String ="BMW"
    
     }
    

    class Johnny{

    val haveTv:String = "Sony"

    }

   val mahadi = new Mahadi



   mahadi.haveTv // Error happening

ここで、 mahadi.haveTvはエラーを生成します。Scalaコンパイラは最初にmahadiレシーバーへのhaveTvプロパティを探すからです。見つかりません。次に、 Mahadiオブジェクトを引数として取り、 Johnnyオブジェクトを返す暗黙のキーワードを持つスコープ内のメソッドを探します。しかし、ここにはありません。したがって、エラーが発生します。しかし、以下は大丈夫です。

class Mahadi{

val haveCar:String ="BMW"

}

class Johnny{

val haveTv:String = "Sony"

}

val mahadi = new Mahadi

implicit def z(a:Mahadi):Johnny = new Johnny

mahadi.haveTv // compiler will use z here like new Johnny().haveTv

println(mahadi.haveTv)// result Sony & no error
  1. 暗黙的なパラメータインジェクション:メソッドを呼び出し、そのパラメータ値を渡さないと、エラーが発生します。Scalaコンパイラーはこのように機能します-最初に値を渡そうとしますが、パラメーターの直接値を取得しません。

     def x(a:Int)= a
    
     x // ERROR happening
    

次に、パラメーターに暗黙のキーワードがある場合、同じタイプの値を持つスコープ内の任意のvalを検索します。取得しない場合はエラーが発生します。

def x(implicit a:Int)= a

x // error happening here

この問題を解決するために、コンパイラーは、パラメーターa暗黙のキーワードが含まれているため、 Intの型を持つ暗黙のvalを探します。

def x(implicit a:Int)=a

implicit val z:Int =10

x // compiler will use implicit like this x(z)
println(x) // will result 10 & no error.

もう一つの例:

def l(implicit b:Int)

def x(implicit a:Int)= l(a)

私たちはそれを次のように書くこともできます-

def x(implicit a:Int)= l

lには暗黙のパラメーターがあり、メソッドxの本体のスコープには、xのパラメーターである暗黙のローカル変数パラメーターはローカル変数aがあります。したがって、xメソッドの本体 、メソッド署名lの暗黙の引数値は次のようになります。 xメソッドのローカル暗黙変数(パラメーター)によって暗黙的にファイルされます。a

それで

 def x(implicit a:Int)= l

このようなコンパイラになります

def x(implicit a:Int)= l(a)

もう一つの例:

def c(implicit k:Int):String = k.toString

def x(a:Int => String):String =a

x{
x => c
}

x {x => c}cは、引数の明示的な値の受け渡しまたはスコープの暗黙のvalを必要とするため、エラーが発生します。

したがって、メソッドxを呼び出すときに、関数リテラルのパラメーターを明示的に暗黙的にすることができます。

x{
implicit x => c // the compiler will set the parameter of c like this c(x)
}

これはPlay-Frameworkのアクションメソッドで使用されています

in view folder of app the template is declared like
@()(implicit requestHreader:RequestHeader)

in controller action is like

def index = Action{
implicit request =>

Ok(views.html.formpage())  

}

リクエストパラメータを明示的に暗黙的に言及していない場合は、次のように記述されている必要があります-

def index = Action{
request =>

Ok(views.html.formpage()(request))  

}
  1. 拡張方法

Integerオブジェクトを使用して新しいメソッドを追加したいと考えてください。メソッドの名前はmeterToCmになります。

> 1 .meterToCm 
res0 100 

これを行うには、オブジェクト/クラス/トレイト内に暗黙のクラスを作成する必要があります。このクラスをケースクラスにすることはできません。

object Extensions{
    
    implicit class MeterToCm(meter:Int){
        
        def  meterToCm={
             meter*100
        }

    }

}

暗黙のクラスは1つのコンストラクターパラメーターのみを受け取ることに注意してください。

次に、使用するスコープに暗黙のクラスをインポートします

import  Extensions._

2.meterToCm // result 200
于 2018-12-13T08:59:59.250 に答える
7

requestパラメータを次のようにマークする必要がある理由と時期implicit

アクションの本体で使用するいくつかのメソッドには、たとえば、Form.scalaがメソッドを定義するような暗黙のパラメーターリストがあります。

def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = { ... }

単に呼び出すので、必ずしもこれに気付く必要はありませmyForm.bindFromRequest()ん。暗黙の引数を明示的に指定する必要はありません。いいえ、リクエストのインスタンスを必要とするメソッド呼び出しに遭遇するたびに、渡す有効な候補オブジェクトを探すためにコンパイラを離れます。利用可能なリクエストがあるので、あなたimplicitがする必要があるのはそれをとしてマークすることだけです。

暗黙的に使用できるものとして明示的にマークします。

Playフレームワークから送信されたリクエストオブジェクト(「request」という名前を付けましたが、必要に応じて「r」または「req」だけを使用できます)を使用しても「OK」であることをコンパイラに示唆します。 。

myForm.bindFromRequest()

見えますか?そこにはありませんが、そこにあります!

必要なすべての場所に手動で挿入しなくても発生します(ただし、マークされているかどうかに関係なく、必要に応じて明示的に渡すことができimplicitます)。

myForm.bindFromRequest()(request)

暗黙的としてマークせずに、上記実行する必要があります。暗黙としてマークする必要はありません。

リクエストをいつマークする必要がありますimplicitか?Requestのインスタンスを期待する暗黙のパラメータリストを宣言するメソッドを使用している場合にのみ、本当に必要です。implicit ただし、簡単にするために、リクエストに常にマークを付ける習慣を身に付けることができます。そうすれば、美しい簡潔なコードを書くことができます。

于 2017-01-03T19:32:34.943 に答える
5

また、上記の場合only one、型が。である陰関数が存在する必要がありdouble => Intます。そうしないと、コンパイラが混乱し、正しくコンパイルされません。

//this won't compile

implicit def doubleToInt(d: Double) = d.toInt
implicit def doubleToIntSecond(d: Double) = d.toInt
val x: Int = 42.0
于 2017-10-25T16:48:48.700 に答える
1

私はあなたとまったく同じ質問をしました、そして私がそれを理解し始めた方法をいくつかの本当に簡単な例で共有する必要があると思います(それは一般的なユースケースのみをカバーしていることに注意してください)。

を使用するScalaの2つの一般的なユースケースがありますimplicit

  • 変数での使用
  • 関数で使用する

例は次のとおりです

変数で使用します。ご覧のとおりimplicit、最後のパラメータリストでキーワードが使用されている場合は、最も近い変数が使用されます。

// Here I define a class and initiated an instance of this class
case class Person(val name: String)
val charles: Person = Person("Charles")

// Here I define a function
def greeting(words: String)(implicit person: Person) = person match {
  case Person(name: String) if name != "" => s"$name, $words"
    case _ => "$words"
}

greeting("Good morning") // Charles, Good moring

val charles: Person = Person("")
greeting("Good morning") // Good moring

関数で使用します。ご覧のとおりimplicit、関数でが使用されている場合は、最も近い型変換方法が使用されます。

val num = 10 // num: Int (of course)

// Here I define a implicit function
implicit def intToString(num: Int) = s"$num -- I am a String now!"

val num = 10 // num: Int (of course). Nothing happens yet.. Compiler believes you want 10 to be an Int

// Util...
val num: String = 10 // Compiler trust you first, and it thinks you have `implicitly` told it that you had a way to covert the type from Int to String, which the function `intToString` can do!
// So num is now actually "10 -- I am a String now!"
// console will print this -> val num: String = 10 -- I am a String now!

これがお役に立てば幸いです。

于 2020-05-11T04:49:30.693 に答える
0

Scalaでの暗黙の非常に基本的な例。

暗黙のパラメータ

val value = 10
implicit val multiplier = 3
def multiply(implicit by: Int) = value * by
val result = multiply // implicit parameter wiil be passed here
println(result) // It will print 30 as a result

注:ここでmultiplierは、暗黙的に関数に渡されますmultiply。関数呼び出しに欠落しているパラメーターは、現在のスコープのタイプによって検索されます。つまり、スコープにInt型の暗黙の変数がない場合、コードはコンパイルされません。

暗黙の変換

implicit def convert(a: Double): Int = a.toInt
val res = multiply(2.0) // Type conversions with implicit functions
println(res)  // It will print 20 as a result

注: double値を渡して関数を呼び出すとmultiply、コンパイラーは現在のスコープで変換暗黙関数を見つけようとします。これは、(関数がパラメーターを受け入れるように)に変換Intされます。暗黙の関数がない場合、コンパイラはコードをコンパイルしません。 DoublemultiplyIntconvert

于 2019-11-30T19:51:27.610 に答える