对于来自Java和其他语言(如C
和C++
)的程序员来说,implicit
关键字是一个非常模糊的东西,因此了解Scala中的implicit
关键字非常重要。在Scala中如何使用implicit
?
大多数情况下,问题是“在Scala中蕴涵的用法是什么?”是在“如何编写/使用隐式转换?”、“如何使用隐式类型类?”的意义上回答的。等等。
对于新的Scala程序员(至少是我认识的那些人)来说,这样的回答大多数时候给人的印象是implicits
实际上只是一个“美化”工具,
val s = (new RichInt(5)).toString + ":: Well"
只是为了
val s = 5 + ":: Well"
一些持“裸机”哲学的程序员甚至倾向于不喜欢隐藏这些细节的implicit
的存在。
不知何故,重要的问题是“在Scala中的”用途和重要性“是什么?”不知怎么的被忽视了也没人回答。
特别是了解Scala的程序员,他们在一定程度上理解“如何使用
implicits
”,并且有时会为这个问题的“隐藏魔法”所困扰--“implicits有什么特别之处,以至于Scala创建者甚至选择使用implicits
?”
我不是很确定OP在作为Scala程序员探索
implicits
时是否也考虑了这些问题。
令人惊讶的是,我在任何地方都找不到这些问题的答案。一个原因是,即使是回答
implicits
的实际“需要/好处”的一个用例,也需要大量的解释。
我认为这些问题值得社区关注,以帮助新的Scala程序员。我试图简单地解释
implicits
的一个用例。我希望看到更多的人(更有见识的人)有兴趣回答这个问题。
我更多的是从“implicits
有什么用途(为什么在某些情况下需要implicit
)”的角度来回答这个问题,而不是从“在某些情况下如何使用implicit
”的角度来回答这个问题(这个问题的答案可以在google上搜索)。
在进行解释时,我在某些地方以可互换的方式使用了type
和class
。它仍然应该以一种简单的方式传达预期的信息,但它可能表明type
和class
是相似的错误概念。我看不出有什么办法可以不对答案进行重大修改就能解决这个问题。请记住,type
和class
是不同的东西。
implicit
的实际功能。
它其实很简单,implicit
做的正是顾名思义。如果需要查找所述类型的“Go to”实例,它将事物标记为相应作用域中的“Go to”实例/值。
因此,只要需要a
类型的“Go To”实例,我们就可以说a
类型的隐式
实例/值就是a
类型的“Go To”实例。
要将任何实例/值标记为相应范围内可用的implicity
(“转到”),我们需要使用implicit
关键字。
scala> implicit val i: Int = 5
// i: Int = 5
我们如何在需要时调用隐式
实例/值?
调用implicit
的最直接方法是使用implictly
方法。
val gotoInt = implicitly[Int]
// gotoInt: Int = 5
或者,我们可以使用implicit
参数定义方法
,以期望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
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
// impl_1
def performMySpecialBehaviour[A <: LibTrait](a): Unit
但是,上面的内容将允许扩展libtrait
的所有内容。
一种选择是为所有“支持的”类定义方法。但是由于您不控制libtrait
的扩展,所以您甚至不能这样做(这也不是一个很好的选择)。
另一个选择是为您的方法建模这些“限制”,
trait MyRestriction[A <: LibTrait] {
def myRestrictedBehaviour(a: A): Unit
}
// impl_2
def performMySpecialBehaviour(mr: MyRestriction): Unit
因此,现在用户首先必须将他们的实例
转换为MyRestriction
的一些实现
(这将确保满足您的限制)。
但是查看PerformMySpecialBehavior
的签名,您将看不到与实际需要的任何相似之处。
此外,您的限制似乎绑定到class
而不是实例本身,因此我们可以使用typeclass
。
// impl_3
def performMySpecialBehaviour[A <: LibTrait](a: A, mr: MyRestriction[A]): Unit
用户可以为其类
定义type-class实例
并将其与方法
一起使用
object myRestrictionForYourClass1 extends MyRestriction[YourClass1] {
def myRestrictedBehaviour(a: A): Unit = ???
}
但是查看PerformMySpecialBehavior
的签名,您将看不到与实际需要的任何相似之处。
但是,如果您要使用考虑implicits
的使用,我们可以更清楚地了解它的用法
// impl_4
def performMySpecialBehaviour[A :< LibTrait](a: A)(implicit ev: MyRestriction[A]): Unit
请记住,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] {...}
...
...
def otherMethod[A <: YoutTrait](a: A): Unit = {
// do something before
val mr: MyRestriction[A] = ??????????
performMySpecialBehaviour(a, mr)
// do something after
}
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
调用的范围中可用,则代码将无法编译。
这只是一个示例,但它应该足以说明implicits
在Scala中的实际用途是“为什么”和“什么
关于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
注::如果您注意到答案有任何问题,请评论。欢迎任何反馈。
问题内容: Node.js module.exports的用途是什么,如何使用它? 我似乎找不到任何相关信息,但是正如我在源代码中经常看到的那样,它似乎是Node.js的重要组成部分。 根据Node.js文档: 模组 对当前的引用 。特别 是与导出对象相同。请参阅 以获取更多信息。 但这并没有真正的帮助。 究竟是做什么的,一个简单的例子是什么? 问题答案: 是调用结果实际返回的对象。 该变量最初设
问题内容: 如何在hibernate中使用级联和逆运算?定义它们的过程/标签是什么?它们彼此相关吗?它们如何有用? 问题答案: 在通过中介表进行多对多关系的情况下;“级联”表示是否在子表中创建/更新记录。而“反向”表示是否在中间表中创建/更新记录 例如,假设情况1下的学生可以拥有多部电话。因此,学生班级拥有“手机套”的属性。另外,一部电话可以由多个学生拥有。因此,“电话”类具有“学生组”的属性。s
所以我知道MobileAds。initialize()用于减少会话第一个ad请求的延迟。但我看到的结构是MobileAds。初始化(context,app_id),那么“initializationStatus”有什么用呢-
对于PL/pgSQL来说是全新的,这个函数中的双美元符号是什么意思: 我猜,在中,是占位符。 最后一行有点玄机:< code > $ $ LANGUAGE plpgsql STRICT IMMUTABLE; 顺便问一下,最后一行是什么意思?
问题内容: 如何在休眠中使用级联和逆运算?定义它们的过程/标签是什么?它们彼此相关吗?它们有什么用? 问题答案: 在通过中介表进行多对多关系的情况下;“级联”表示是否在子表中创建/更新记录。而“反向”表示是否在中间表中创建/更新记录 例如,假设在方案1下,一个学生可以拥有多部电话。因此,学生班级拥有“手机套”属性。另外,一部电话可以由多个学生拥有。因此,“电话”类具有“学生组”的属性。stud_p
iOS 6和Xcode 4.5有一项新功能,称为“Unwind Segue”: 展开分段可以允许转换到故事板中的现有场景实例 除了Xcode 4.5发行说明中的这一简短条目外,UIViewController现在似乎有两个新方法: 展开段如何工作以及它们可以用于什么?