5.6 隐式函数

在本教程中,我们将学习如何创建隐式函数。通过使用隐式函数,我们可以为几乎任何类型或类提供扩展方法或函数。 顾名思义,Scala从一开始就是可扩展的。隐式的用法,是Scala提供的特性之一,通过它我们可以轻松地向任何类型或类添加扩展方法或函数。

在下面这个示例中,我们将扩展String类,使其具有isFavoriteFruit()函数。

object demo04 {

// 创建一个简单的包装类FruitString,
// 它将使用String类型作为参数,然后提供一个isFavoriteDonut()函数。
class FruitString(s: String) {
  def isFavoriteFruit: Boolean = s == "苹果"
}

// 创建隐式函数转换字符串到包装字符串类
// 将隐式函数或转换封装到一个使用对象的单例中是一个很好的实践
// 义一个名为stringToFruitString的隐式函数,该函数将以String类型作为参数,
// 并通过名为FruitString的String包装器类的新实例将其连接起来。
object FruitConverstions {
  // 定义隐式函数类似于定义任何其他函数,只是使用implicit关键字作为函数签名的前缀。
  implicit def stringToFruitString(s: String) = new FruitString(s)
}

def main(args: Array[String]): Unit = {
  // 为了使用隐式String函数将String类型转换为FruitString类型,
  // 必须在作用域内使用隐式函数。这可以使用import关键字实现
  import FruitConverstions._

  // 创建两个String类型的不可变值,分别代表不同的水果
  val apple = "苹果"
  val gooseberry = "弥猴桃"

  // 访问 isFavoriteFruit()
  println(s"苹果是不是最喜欢的水果 = ${apple.isFavoriteFruit}")
  println(s"弥猴桃是不是最喜欢的水果 = ${gooseberry.isFavoriteFruit}")
}
}

执行上面的代码,输出内容如下:

苹果是不是最喜欢的水果 = true
弥猴桃是不是最喜欢的水果 = false

Tip:

  • 将隐式函数和值封装到Object或Package Object中是一个很好的实践。

  • Scala Predef类利用隐式函数提供现成的转换,比如Java从/到Scala的转换。

隐式值

隐式参数的使用是Scala中如何实现依赖注入的一个例子。事实上,依赖注入是内置在Scala语言中的,这样就不必导入另一个第三方库。

在下面的示例中,定义了一个函数,带有一个隐式参数。在执行时,Scala编译器将寻找Double类型的隐式值。如果作用域中没有隐式值,则会得到一个编译器错误。因此,我们将在代码库中某处定义一个Double类型的隐式值。定义隐式值类似于使用val关键字定义任何其他值,只是在val关键字前面加上implicit关键字。

object demo {
def main(args: Array[String]): Unit = {
  // 定义一个Double类型的隐式值
  implicit val discount: Double = 0.1

  println(s"所有客户将收到一个 ${discount * 100}% 折扣")
  println(s"""加上5个苹果的折扣价 = ${totalCost("苹果", 5)}""")
}

// 定义一个带有隐式参数的函数
// 在执行时,Scala编译器将寻找Double类型的隐式值。
// 如果作用域中没有隐式值,则会得到一个编译器错误。
def totalCost(fruitType: String, quantity: Int)(implicit discount: Double): Double = {
  println(s"计算 $quantity 个 $fruitType 的价格")
  val totalCost = 2.50 * quantity * (1 - discount)
  totalCost
}
}

如何定义一个函数接受多个隐式参数?

定义额外的隐式参数类似于定义任何其他参数,只需使用逗号分隔参数。在下面的例子中,我们扩展totalCost()函数,以接受一个隐含的String类型参数,该参数表示水果商店的名称。

因此,需要首先定义另一个String类型的隐式值,以便它在调用totalCost()函数之前处于作用域中。

object demo {
def main(args: Array[String]): Unit = {
  // 定义一个Double类型的隐式值
  implicit val discount: Double = 0.1

  println(s"所有客户将收到一个 ${discount * 100}% 折扣")
  println(s"""加上5个苹果的折扣价 = ${totalCost("苹果", 5)}""")

  implicit val storeName: String = "农夫果园"
  println(s"""加上5个苹果的折扣价 = ${totalCost2("苹果", 5)}""")
}

// 定义一个带有隐式参数的函数
// 在执行时,Scala编译器将寻找Double类型的隐式值。
// 如果作用域中没有隐式值,则会得到一个编译器错误。
def totalCost(fruitType: String, quantity: Int)(implicit discount: Double): Double = {
  println(s"计算 $quantity 个 $fruitType 的价格")
  val totalCost = 2.50 * quantity * (1 - discount)
  totalCost
}

// 多个隐式参数
def totalCost2(fruitType: String, quantity: Int)(implicit discount: Double, storeName: String): Double = {
  println(s"[$storeName] 计算 $quantity 个 $fruitType 的价格")
  val totalCost = 2.50 * quantity * (1 - discount)
  totalCost
}
}

如何手动传递隐式参数

在极少数情况下,我们可能不得不手动传递隐式参数值。这可以通过下面所示的另一对括号传递隐式参数来实现。

// 手动传递隐式参数
println(s"""加上5个苹果的折扣价, 手动传参 = ${totalCost2("苹果", 5)(0.1, "农夫果园")}""")
© 版权声明
THE END
喜欢就支持一下吧
点赞136赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

夸夸
夸夸
还有吗!没看够!
取消
昵称表情代码图片

    暂无评论内容