我正在通过Scala playframework教程,我遇到了这段令我困惑的代码片段:
def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
errors => BadRequest(views.html.index(Task.all(), errors)),
label => {
Task.create(label)
Redirect(routes.Application.tasks())
}
)
}
所以我决定调查并遇到this post .
我仍然没有得到它 .
这有什么区别:
implicit def double2Int(d : Double) : Int = d.toInt
和
def double2IntNonImplicit(d : Double) : Int = d.toInt
除了明显的事实,他们有不同的方法名称 .
我什么时候应该使用 implicit
?为什么?
4 回答
我将在下面解释implicits的主要用例,但有关详细信息,请参阅relevant chapter of Programming in Scala .
Implicit parameters
方法的最终参数列表可以标记为
implicit
,这意味着将从调用它们的上下文中获取值 . 如果范围中没有正确类型的隐式值,则不会编译 . 由于隐式值必须解析为单个值并避免冲突,因此需要您的方法来查找隐式Int
!例:
Implicit conversions
当编译器为上下文找到错误类型的表达式时,它将查找允许其进行类型检查的类型的隐式
Function
值 . 因此,如果需要A
并找到B
,它将在范围内查找类型为B => A
的隐式值(它还会检查其他位置,如B
和A
伴随对象(如果存在)) . 由于def
可以"eta-expanded"到Function
对象,implicit def xyz(arg: B): A
也可以 .因此,您的方法之间的区别在于,当找到
Double
但需要Int
时,编译器将为您插入标记为implicit
的那个 .会像以前一样工作
在第二个我们手动插入转换;在第一个编译器自动完成相同的操作 . 由于左侧的类型注释,因此需要转换 .
关于Play的第一个片段:
Play文档中的this page解释了操作(另请参阅API docs) . 您正在使用
在
Action
对象上(它是同名特征的伴侣) .所以我们需要提供一个Function作为参数,可以在表单中写成文字
在函数文字中,
=>
之前的部分是值声明,如果需要,可以标记为implicit
,就像在任何其他val
声明中一样 . 在这里,request
不必标记为implicit
,因此要进行类型检查,但通过这样做,它将作为函数中可能需要它的任何方法的隐式值(当然,它可以明确地用作好) . 在这种特殊情况下,这已经完成,因为Form类上的bindFromRequest
方法需要隐式的Request
参数 .WARNING: 明智地包含讽刺!因人而异...
Luigi's answer完整无误 . 这个只是为了扩展它有一个例子,你可以如何过度使用implicits,因为它经常出现在Scala项目中 . 实际上,你甚至可以在一个"Best Practice"指南中找到它 .
为什么以及何时将
request
参数标记为implicit
:您将在动作的主体中使用的一些方法具有 implicit parameter list 之类的,例如,Form.scala定义了一个方法:
您不一定会注意到这一点,因为您只需调用
myForm.bindFromRequest()
您不必显式提供隐式参数 . 不,您每次遇到需要请求实例的方法调用时,都会让编译器查找要传递的任何有效候选对象 . 由于您确实有可用的请求,因此您只需将其标记为implicit
即可 .您明确将其标记为可用于隐式使用 .
你暗示编译器使用Play框架发送的请求对象(我们给出的名称为“request”,但可能只使用了“r”或“req”),“在狡猾的地方”是“OK” .
看见?它不存在,但它就在那里!
它只是在没有你需要在每个需要的地方手动插入它的情况下发生(但你可以明确地传递它,如果你愿意,无论它是否标记为
implicit
):如果不将其标记为隐式,您将 have to 执行上述操作 . 将其标记为隐含的您不必 .
When 你应该将请求标记为
implicit
吗?如果您正在使用声明一个期望Request实例的隐式参数列表的方法,那么您才真正需要 . 但为了保持简单,你可以养成标记请求的习惯implicit
always . 这样你就可以编写漂亮的简洁代码 .此外,在上述情况下,应该有
only one
隐式函数,其类型为double => Int
. 否则,编译器会感到困惑,无法正确编译 .