Scala的implicit和implicitly

Posted ScalaCoder

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala的implicit和implicitly相关的知识,希望对你有一定的参考价值。

这篇文章主要通过使用implicit和implicitly,了解它们的主要用途,和区别。

以下均是代码示例配合注释,大屏更适合观看。


//隐式方法是标准库实现的,使用了内联函数//隐式方法通常用于检查类型的隐式值是否可用,并在这种情况下返回它。@inline def implicitly[T](implicit e: T): T = e


下面有一些例子:

/** * 隐式参数和隐式方法 * * 两者之间在功能上没有差异 * * 隐式参数不会直接在当前的方法体中使用,而是传递给另一个具有相同隐式参数列表的函数 * 隐式参数是Scala关键字支持的 * * 若想使用隐式参数,一般使用implicitly * 若只是需要隐式参数,为了满足调用参数列表的语义,而不在方法内使用隐式参数本身,一般使用implicit * * @author 梦境迷离 dreamylost * @since 2020-07-05 * @version v1.0 */object ImplicitTest extends App {
/** * 隐式参数最常用的用途之一:通过柯里化给已知方法增加第二个参数列表,而不影响方法原有参数列表,并且将具体参数与拓展参数分开 * 国际化在这里是一个后加的参数,且是隐式的。test1函数的调用方可以继续像之前没有locale参数一样,使用test1函数,只需要在方法调用前准备好可用的隐式参数。 * 当然因为是柯里化的,调用方也可选择显示的传递locale参数,一般不需这样,除非当多个隐式参数可用时,为了消除歧义可能需要这样。 * 在这里,国际化语言一般从每个用户请求中解析,仅有一个。 * * @param x * @param locale */ def test1(x: String)(implicit locale: Locale): Unit = { def test1Helper(args: String)(implicit locale: Locale): Unit = { println(s"args=$args, locale=$locale") }
test1Helper(x) }

//显示调用隐式参数,隐式参数的柯里化不能使用部分参数传递 test1("hello world")(Locale.CHINA)

def test2(x: String)(y: String): Unit = { println(s"x=$x, y=$y") }
//对于非隐式参数的柯里化函数,可以仅使用第一个参数列表,此时fun是一个 String => Unit 的函数,还需要接收一个String参数才能执行println //后面的下划线表示,对该函数的调用尚未结束,还需继续调用 val fun = test2("hello-") _
fun("world")

//并且对隐式参数,参数名称是无关的,也就是你可以给隐式参数取任意合法的变量名字 implicit val china = Locale.CHINA test1("hello world2")}
object ImplicitTest2 extends App {
/** spec 1 **/ trait Show[T] { def show(t: T): String }
//伴生对象中的隐式参数对trait可见 object Show { //与val相同,但是def支持泛型,都称为隐式值,当在参数列表时,称为隐式参数 implicit def IntShow: Show[Int] = (i: Int) => i.toString
implicit def StringShow: Show[String] = (s: String) => s
}
case class Person(name: String, age: Int)
object Person { def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = //使用了隐式si ss,会处理Int和String类型 (p: Person) => "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")" }
//使用implicitly自动搜索隐式参数,因为implicit si: Show[Int], ss: Show[String]都是在Show的伴生对象中。 //调用Person的方法手动获取show对象 //若Show伴生对象无si ss隐式参数,则implicitly会无法编译 val show1 = Person.PersonShow(si = implicitly, ss = implicitly) val ret1 = show1.show(Person("bob", 25)) println(ret1)

/** spec 2 **/ trait Show2[T] { def show(t: T): String }
object Show2 { //与val相同,但是def支持泛型,都称为隐式值,当在参数列表时,称为隐式参数 implicit def IntShow: Show2[Int] = (i: Int) => i.toString
implicit def StringShow: Show2[String] = (s: String) => s
}
case class Person2(name: String, age: Int)
object Person2 { //对于PersonShow2本身也是隐式参数的,可以直接使用隐式方法implicitly,自动得到Show[Person2]对象 implicit def PersonShow2(implicit si: Show2[Int], ss: Show2[String]): Show2[Person2] = (p: Person2) => "Person2(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")" }
val show2: Show2[Person2] = implicitly[Show2[Person2]] val ret2 = show2.show(Person2("bob", 25)) println(ret2)

/** spec 3 **/ trait Show3[T] { def show(t: T): String }
object Show3 { //与val相同,但是def支持泛型,都称为隐式值,当在参数列表时,称为隐式参数 implicit def IntShow: Show3[Int] = (i: Int) => i.toString
implicit def StringShow: Show3[String] = (s: String) => s
}
case class Person3(name: String, age: Int)
object Person3 { def PersonShow3(implicit si: Show3[Int], ss: Show3[String]): Show3[Person3] = (p: Person3) => "Person3(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")" }

val show3 = Person3.PersonShow3 //这里不用使用参数也可以,隐式参数在Show3伴生对象中 val ret3 = show3.show(Person3("bob", 25)) println(ret3)

/** spec 4 **/ trait Show4[T] { def show(t: T): String }
case class Person4(name: String, age: Int)
object Person4 { def PersonShow4(implicit si: Show4[Int], ss: Show4[String]): Show4[Person4] = (p: Person4) => "Person4(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")" }

implicit val stringShow: Show4[Int] = (s: Int) => s.toString implicit val intShow: Show4[String] = (s: String) => s val show4 = Person4.PersonShow4 val ret4 = show4.show(Person4("bob", 25)) println(ret4)

/** spec 5 **/ case class Person5(name: String, age: Int)
trait Show5[T] { def show(t: T): String }
object Person5 { def PersonShow5(implicit si: Show5[Int], ss: Show5[String]): Show5[Person5] = (p: Person5) => "Person5(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")" }

//在这里,使用def与泛型,只需要定义一个隐式值 implicit def typeShow[T]: Show5[T] = (s: T) => s"hello implicit 5 ${s.toString}"
//此时根据最相近匹配,会继续使用 stringShow2 intShow2的,typeShow隐式无效 implicit val stringShow2: Show5[Int] = (s: Int) => s.toString implicit val intShow2: Show5[String] = (s: String) => s val show5 = Person5.PersonShow5 val ret5 = show5.show(Person5("bob", 25)) println(ret5)

/** spec 6 **/ trait Show6[T] { def show(t: T): String }
case class Person6(name: String, age: Int)
object Person6 { def PersonShow6[T](implicit si: Show6[Int], ss: Show6[String]): Show6[Person6] = (p: Person6) => "Person6(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")" }

implicit def typeShow2[T]: Show6[T] = (s: T) => s"hello implicit 6 ${s.toString}"
val show6 = Person6.PersonShow6 val ret6 = show6.show(Person6("bob", 25)) println(ret6)}


输出

Person(name=bob, age=25)
Person2(name=bob, age=25)
Person3(name=bob, age=25)
Person4(name=bob, age=25)
Person5(name=bob, age=25)
Person6(name=hello implicit 6 bob, age=hello implicit 6 25)


以上是关于Scala的implicit和implicitly的主要内容,如果未能解决你的问题,请参考以下文章

scala中的implicit

scala 学习总结: implicit 函数的使用

在 scala 中导入 spark.implicits._

Scala 学习笔记之implicit

Spark scala 模拟 spark.implicits 用于单元测试

scala 隐式详解(implicit关键字)