类型化函数,多态函数和符号函数
类型化函数
所谓类型化函数,指的是在调用函数时可以指定参数的类型。Scala的类型化函数通过变量的使用提供了更大的灵活性。那么,如何定义一个泛型类型函数将指定其参数的类型?
下面我们将创建一个类型化函数,它将指定一个T类型的泛型参数,如下所示:
object demo { // 应用折扣的泛型函数 def applyDiscount[T](discount: T) { discount match { case d: String => println(s"在数据库中查找 $d 的百分比折扣") case d: Double => println(s"$d 折扣优惠") case _ => println("不支持的折扣类型") } } def main(args: Array[String]): Unit = { // 要调用类型化函数applyDiscount(),必须提供其参数的类型。 applyDiscount[String]("COUPON_123") applyDiscount[Double](10) applyDiscount[Int](10) } }
执行上面的代码,输出内容如下:
在数据库中查找 COUPON_123 的百分比折扣 10.0 折扣优惠 不支持的折扣类型
虽然可以依赖Scala的类型推断来推断函数参数的类型,但一般来说,显式指定参数类型是一个很好的实践。
多态函数
多态函数指的是具有泛型返回类型的函数。在多态函数中,我们可以指定参数的类型以及函数的返回类型。
下面让我们定义另一个本质上是多态的函数。我们将函数命名为applyDiscountWithReturnType(),它将接受泛型类型T作为其折扣参数。但是它也会返回一个类型为T的Sequence。
object demo { // 定义一个多态函数 // 它将接受泛型类型T作为其折扣参数。但是它也会返回一个类型为T的Sequence。 def applyDiscountWithReturnType[T](discount: T): Seq[T] = { discount match { case d: String => println(s"在数据库中查找 $d 的百分比折扣") Seq[T](discount) case d: Double => println(s"$d 折扣优惠") Seq[T](discount) case d @ _ => println("不支持的折扣类型") Seq[T](discount) } } def main(args: Array[String]): Unit = { // 使用不同的类型,即String、Double和Char来分别调用多态函数 println(s"带String参数调用结果 = ${applyDiscountWithReturnType[String]("COUPON_123")}") println() println(s"带Double参数调用结果 = ${applyDiscountWithReturnType[Double](10.5)}") println() println(s"带Char参数调用结果 = ${applyDiscountWithReturnType[Char]('U')}") } }
执行以上代码,输出结果如下:
在数据库中查找 COUPON_123 的百分比折扣 带String参数调用结果 = List(COUPON_123) 10.5 折扣优惠 带Double参数调用结果 = List(10.5) 不支持的折扣类型 带Char参数调用结果 = List(U)
为简单起见,我们将为模式匹配中的每一种情况返回一个类型为T的新序列。对于调用多态函数applyDiscountWithReturnType()时使用的每个指定类型,该函数还返回相同类型的Sequence序列。
关于@运算符
@运算符允许将匹配的模式绑定到变量。请看下面的示例:
object demo { def main(args: Array[String]): Unit = { // 例如,考虑以下情况: val o: Option[Int] = Some(2) // 可以很容易地提取内容: o match { case Some(x) => println(x) case None => } // 但是,如果想要的不是Some的内容,而是Option本身呢? // 这将通过以下方式实现: o match { case x @ Some(_) => println(x) case None => } } }
执行上面的代码,输出结果如下:
2 Some(2)
注意,@ 可用于任何级别,而不仅仅是顶级的匹配。
符号函数
符号函数指的是仅使用符号而不是字母命名的函数。当必须创建特定于域的语言语法时,Scala的这个特性是一个很好的构建块。
定义名称为符号的函数本质上与定义任何其他函数相同。这个函数没有名称,而是用一些符号来定义。例如:
def -(discount: Double): Double = { totalCost - discount }
调用一个函数,其名称只是一个符号,就像上面步骤中的-函数一样,与调用任何其他函数没有什么不同。
println(s"调用函数 - = ${donutCostCalculator.-(10.5)}")
Scala允许使用如下所示的操作符样式来调用函数:
println(s"调用函数-使用操作符样式表示法 = ${donutCostCalculator - 10.5}")
使用操作符样式调用函数与调用函数没有太大区别,只是不需要指定点(.)。当调用函数名只是符号时,使用操作符风格更清晰。
既然已经知道了如何定义名称只是符号的函数,那么让我们创建另一个名为+++的函数。
def +++(taxAmount: Double): Double = { totalCost + taxAmount }
完整代码如下:
object demo { val totalCost = 100 // 定义名称为符号的函数本质上与定义任何其他函数相同。这个函数没有名称,而是用一些符号来定义。例如: def -(discount: Double): Double = { totalCost - discount } // 既然已经知道了如何定义名称只是符号的函数,那么创建另一个名为+++的函数。 def +++(taxAmount: Double): Double = { totalCost + taxAmount } def main(args: Array[String]): Unit = { // 调用一个函数,其名称只是一个符号,就像上面步骤中的-函数一样,与调用任何其他函数没有什么不同。 println(s"调用函数 - = ${demo07.-(10.5)}") // Scala允许使用如下所示的操作符样式来调用函数 // 使用操作符样式调用函数与调用函数没有太大区别,只是不需要指定点(.)。 // 当调用函数名只是符号时,使用操作符风格更清晰。 println(s"调用函数-使用操作符样式表示法 = ${demo07 - 10.5}") // 加税计算的方法 println(s"调用+++函数 = ${demo07 +++ 13.5}") } }
执行以上代码,输出结果如下:
调用函数 - = 89.5 调用函数-使用操作符样式表示法 = 89.5 调用+++函数 = 113.5