我更多是从“implicits 的用途是什么(为什么在某些情况下需要implicit)”而不是 “如何使用implicit 在某些情况下”(可以通过在谷歌上搜索找到答案)。
在解释时,我不知何故最终在某些地方以可互换的方式使用了type 和class。它仍应以简单的方式传达预期的信息,但它可能表明type 和class 相似的误解。如果不对答案进行重大更改,我看不到解决此问题的方法。请记住,type 和 class 不是同一个东西。
implicit 的实际作用。
其实很简单,implicit 就是顾名思义。如果需要查找所述类型的“转到”实例,它会将事物标记为相应范围内的“转到”实例/值。
所以我们可以说A 类型的implicit 实例/值是A 类型的“转到”实例,只要需要A 类型的“转到”实例。
要将任何实例/值标记为在相应范围内可用的implicitly(“转到”),我们需要使用implicit关键字。
scala> implicit val i: Int = 5
// i: Int = 5
我们如何在需要时调用implicit 实例/值?
召唤implicit 最直接的方法是使用implictly 方法。
val gotoInt = implicitly[Int]
// gotoInt: Int = 5
或者我们可以使用implicit 参数定义我们的methods 以期望implicit 实例在它们所使用的范围内的可用性,
def addWithImplictInt(i: Int)(implicit j: Int): Int = i + j
请记住,我们也可以在不指定 implicit 参数的情况下定义相同的方法,
def addWithImplicitInt(i: Int): Int = {
val implictInt = implicitly[Int]
i + implictInt
}
请注意,带有implicit 参数的第一个选项让用户清楚地知道method 需要隐式参数。由于这个原因,implicit 参数应该是大多数情况下的选择(总是有例外)。
为什么我们实际使用implicit?
这与我们使用implicit 值的可能方式有点不同。我们正在谈论为什么我们“实际上”需要使用它们。
答案是帮助编译器确定type 并帮助我们编写类型安全的代码来解决问题,否则会导致运行时类型比较,我们最终会失去编译器可以提供给我们的所有帮助与。
考虑以下示例,
假设我们正在使用一个定义了以下类型的库,
trait LibTrait
case class LibClass1(s: String) extends LibTrait
case class LibClass2(s: String) extends LibTrait
case class LibClass3(s: String) extends LibTrait
case class LibClass4(s: String) extends LibTrait
考虑到这是一个开放的trait,您和其他任何人都可以定义自己的类来扩展这个LibTrait。
case class YourClass1(s: String) extends LibTrait
case class YourClass2(s: String) extends LibTrait
case class OthersClass1(s: String) extends LibTrait
case class OthersClass2(s: String) extends LibTrait
现在,我们要定义一个 method,它仅适用于 LibTrait 的部分实现(仅适用于具有某些特定属性的那些,因此可以执行您需要的特殊行为)。
// impl_1
def performMySpecialBehaviour[A <: LibTrait](a): Unit
但是,以上将允许扩展 LibTrait 的所有内容。
一种选择是为所有“支持的”类定义方法。但是由于你不控制LibTrait的扩展,你甚至不能这样做(这也不是一个非常优雅的选择)。
另一种选择是为您的方法建模这些“限制”,
trait MyRestriction[A <: LibTrait] {
def myRestrictedBehaviour(a: A): Unit
}
现在,只有支持此特定行为的 LibTrait 的子类型才能提出 MyRestriction 的实现。
现在,以最简单的方式,您使用 this 定义您的方法,
// impl_2
def performMySpecialBehaviour(mr: MyRestriction): Unit
所以,现在用户首先必须将他们的instances 转换为MyRestriction 中的一些implementation(这将确保满足您的限制)。
但是查看performMySpecialBehaviour 的签名,您不会看到与您真正想要的任何相似之处。
此外,您的限制似乎绑定到 class 而不是实例本身,因此我们可以继续使用 type class。
// impl_3
def performMySpecialBehaviour[A <: LibTrait](a: A, mr: MyRestriction[A]): Unit
用户可以为他们的class 定义类型类instance 并将它与您的method 一起使用
object myRestrictionForYourClass1 extends MyRestriction[YourClass1] {
def myRestrictedBehaviour(a: A): Unit = ???
}
但是查看performMySpecialBehaviour 的签名,您不会看到与您实际想要的任何相似之处。
但是,如果您要使用,请考虑使用implicits,我们可以更清楚地了解用法
// impl_4
def performMySpecialBehaviour[A :< LibTrait](a: A)(implicit ev: MyRestriction[A]): Unit
但我仍然可以像在 impl_3 中一样传递类型类实例。那么为什么要implicit?
是的,那是因为示例问题太简单了。让我们添加更多内容。
请记住,LibTrait 仍然可以扩展。让我们考虑一下您或您团队中的某个人最终获得了关注,
trait YoutTrait extends LibTrait
case class YourTraitClass1(s: String) extends YoutTrait
case class YourTraitClass2(s: String) extends YoutTrait
case class YourTraitClass3(s: String) extends YoutTrait
case class YourTraitClass4(s: String) extends YoutTrait
注意YoutTrait 也是一个开放特征。
因此,每个MyRestriction 都有自己对应的实例,
object myRestrictionForYourTraitClass1 extends MyRestriction[YourTraitClass1] {...}
object myRestrictionForYourTraitClass2 extends MyRestriction[YourTraitClass1] {...}
...
...
你还有另一个方法,它调用performMySpecialBehaviour
def otherMethod[A <: YoutTrait](a: A): Unit = {
// do something before
val mr: MyRestriction[A] = ??????????
performMySpecialBehaviour(a, mr)
// do something after
}
现在,您如何选择要提供的 MyRestriction 实例。问题是,您仍然可以通过提供Map 以Class 作为键和MyRestriction 实例作为所有types 的值来以一种迂回的方式进行操作。但这是一个丑陋的 hack,它在编译时不会生效。
但是如果您使用基于impl_4 的implict,您的相同方法将如下所示,
def otherMethod[A <: YoutTrait](a: A)(implicit ev: MyRestriction[A]): Unit = {
// do something before
performMySpecialBehaviour(a)
// do something after
}
只要YoutTrait 的所有子类型的MyRestriction 实例在范围内,它就可以工作。否则代码将无法编译。
因此,如果有人添加了 YoutTrait 的新子类型 YourTraitClassXX 并忘记确保已定义 MyRestriction[YourTraitClassXX] 实例并使其在任何 otherMethod 调用的范围内可用,则代码将编译失败。
这只是一个示例,但足以说明“为什么”和“什么”是 Scala 中 implicits 的实际用途
关于implicits 还有很多话要说,但这会让答案太长;它已经是。
此示例中的用例对参数类型 A 进行了限制,以在范围内拥有 type class TypeClass[A] 的实例。它被称为Context Bound,这些方法通常写成,
def restricted[A, B](a: A)(implicit ev: TypeClass[A]): B
或者,
def restricted[A: TypeClass, B](a: A): B
注意:如果您发现答案有任何问题,请发表评论。欢迎任何反馈。