我喜欢隐式定义。它们使代码看起来很好,它们使用户感觉某些功能在类中是自然可用的,而它只是一个隐含的定义。然而,我在考虑JS原型,你可以基本上在你没写的类上定义一个方法。但是,如果在下一个版本中,这个类定义了一个具有相同签名的方法并对其行为做出假设,那么你就搞砸了。
Scala的implicits能够做出几乎相同的事情,只有一个主要区别:隐式定义是作用域的,因此类的作者没有风险让代码通过其他人的隐式定义注入&# 39; s代码。但是用户的代码怎么样?是否保护他免受课堂上的变化?
让我们考虑一下这段代码:
class HeyMan {
def hello = println("Hello")
}
object Main extends App {
val heyMan = new HeyMan
implicit class ImplicitHeyMan(heyMan: HeyMan) {
def hello = println("What's up ?")
}
heyMan.hello // prints Hello
}
非常糟糕,不是吗?对我来说,正确的行为应该是隐式定义总是隐藏真实的定义,以便保护用户代码免受他调用的API中新方法的出现。
你怎么看?有没有办法让它安全或者我们是否应该停止使用这种方式?答案 0 :(得分:11)
非常明确地定义了关于隐式转换的语言行为:
如果在类
m
的对象o
上调用方法C
,并且该类不支持方法m
,那么Scala将寻找隐式从C
转换为 支持m
的内容。
http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html
换句话说,如果heyMan
的(静态已知)类/特征已经定义了方法{{},则隐式转换将永远不会应用于表达式heyMan.hello
中的heyMan
。 1}} - 仅当您调用未已定义的方法时,才会尝试隐式转换。
对我而言,正确的行为应该是隐式定义总是隐藏真实定义,以便保护用户代码免受他所调用的API中新方法的出现。
不是相反的情况同样如此吗?如果隐式转换确实优先,那么用户将面临他们长期定义的方法的危险,这些方法已经存在了5年,突然被新版本的库依赖中的新隐式转换所掩盖。
与用户对新方法的显式定义优先的情况相比,这种情况似乎很多更加隐蔽且难以调试。
有没有办法让它安全或者我们是否应该停止使用这种方式?
如果获得隐式行为非常关键,也许您应该使用显式类型强制隐式转换:
hello
从评论中的(扩展)对话中,您似乎想要一种方法来检查基础类是否不会通过隐式转换来定义您正在使用的方法。
我认为下面的Łukasz的评论是正确的 - 这是你应该在测试中捕获的东西。具体来说,您可以使用ScalaTest's assertTypeError
。只是尝试调用隐式范围之外的方法,它应该无法键入check(并通过测试):
object Main extends App {
val heyMan = new HeyMan
implicit class ImplicitHeyMan(heyMan: HeyMan) {
def hello = println("What's up ?")
}
heyMan.hello // prints Hello
val iHeyMan: ImplicitHeyMan // force conversion via implicit
iHeyMan.hello // prints What's up
}