样例类

样例类会在编译时自动添加好些东西

另外一个为函数式编程提供支持的就是样例类。一个样例类具有一个普通类所有的功能,并且还有很多特别的,当编译器看到case关键字,会进行很多处理:

  • 默认情况下,样例类的构造器参数是 public val 字段,会对每个参数生成访问方法。
  • 在这个类的伴生对象中会创建 apply 方法。
  • 在这个类的伴生对象中会创建 unapply 方法。
  • 在类中生成 copy 方法。
  • 生成 equals 和 hashCode 方法。
  • 生成 toString 方法
apply方法和不可变参数
scala> case class Person(name: String, relation: String)
defined class Person

// "new" not needed before Person
scala> val christina = Person("Christina", "niece")
christina: Person = Person(Christina,niece)
scala> christina.name
res0: String = Christina
// can't mutate the `name` field
scala> christina.name = "Fred"
<console>:10: error: reassignment to val
       christina.name = "Fred"
                  ^
unapply方法:
trait Person {
    def name: String
}
case class Student(name: String, year: Int) extends Person
case class Teacher(name: String, specialty: String) extends Person
def getPrintableString(p: Person): String = p match {
    case Student(name, year) =>
        s"$name is a student in Year $year."
    case Teacher(name, whatTheyTeach) =>
        s"$name teaches $whatTheyTeach."
}

Scala标准的 unapply 方法返回 Option包装起来的 样例类构造字段的 tuple形式。

copy 方法

克隆一个对象,或者 在克隆的过程中更改字段 ,copy是很有用的。

scala> case class BaseballTeam(name: String, lastWorldSeriesWin: Int)
defined class BaseballTeam

scala> val cubs1908 = BaseballTeam("Chicago Cubs", 1908)
cubs1908: BaseballTeam = BaseballTeam(Chicago Cubs,1908)

scala> val cubs2016 = cubs1908.copy(lastWorldSeriesWin = 2016)
cubs2016: BaseballTeam = BaseballTeam(Chicago Cubs,2016)

因为在函数式编程中你不会改变数据,这就是从已经存在的示例中创建一个新的很好的方法。

equals 和 hashCode
scala> case class Person(name: String, relation: String)
defined class Person

scala> val christina = Person("Christina", "niece")
christina: Person = Person(Christina,niece)

scala> val hannah = Person("Hannah", "niece")
hannah: Person = Person(Hannah,niece)

scala> christina == hannah
res1: Boolean = false

有助于你进行对象间的比较,还有存放对象在 Set 和 Map。

样例类的模式匹配对进行函数式编程帮助很大。

样例对象

object

在进入 case object 之前,我们先看一下Scala中的普通 object。
当你想创建一个单例对象时可以使用 object 。一个类的单个实例不相关的方法和值,属于单例对象;用object取代class来实现单例对象。
一个常见的例子是当你创建工具对象时:

object PizzaUtils {
    def addTopping(p: Pizza, t: Topping): Pizza = ...
    def removeTopping(p: Pizza, t: Topping): Pizza = ...
    def removeAllToppings(p: Pizza): Pizza = ...
}
object FileUtils {
    def readTextFileAsString(filename: String): Try[String] = ...
    def copyFile(srcFile: File, destFile: File): Try[Boolean] = ...
    def readFileToByteArray(file: File): Try[Array[Byte]] = ...
    def readFileToString(file: File): Try[String] = ...
    def readFileToString(file: File, encoding: String): Try[String] = ...
    def readLines(file: File, encoding: String): Try[List[String]] = ...
}
case object

一个 case object 很像 object ,但还有更多的特性:

  • 它是可序列化的
  • 它有一个默认的 hashCode 实现
  • 它有一个改进的 toString 方法
    由于这些特点,case object 常用于下面两个方面:
  • 当创建枚举时
  • 当创建消息的“容器”,用于在与别的对象之前传递时
用case object 创建枚举
sealed trait Topping
case object Cheese extends Topping
case object Pepperoni extends Topping
case object Sausage extends Topping
case object Mushrooms extends Topping
case object Onions extends Topping

sealed trait CrustSize
case object SmallCrustSize extends CrustSize
case object MediumCrustSize extends CrustSize
case object LargeCrustSize extends CrustSize

sealed trait CrustType
case object RegularCrustType extends CrustType
case object ThinCrustType extends CrustType
case object ThickCrustType extends CrustType
case class Pizza (
    crustSize: CrustSize,
    crustType: CrustType,
    toppings: Seq[Topping]
)
用 case object 作为消息

加入你要创建一个亚马逊的 Alexa 应用,你想要传递说话消息,像 “speak the enclosed text,” “stop speaking,”, “pause,” 和“resume.” ,你可以这样写:

case class StartSpeakingMessage(textToSpeak: String)
case object StopSpeakingMessage
case object PauseSpeakingMessage
case object ResumeSpeakingMessage

StartSpeakingMessage 是 case class 。因为 case object 不能有构造器参数。
如果有用到Akka库,可以这样写:

class Speak extends Actor {
  def receive = {
    case StartSpeakingMessage(textToSpeak) =>
        // code to speak the text
    case StopSpeakingMessage =>
        // code to stop speaking
    case PauseSpeakingMessage =>
        // code to pause speaking
    case ResumeSpeakingMessage =>
        // code to resume speaking
  }
}

这是一个很好的安全的在Scala程序之间传递消息的方式。

scala中的函数错误处理

因为函数式编程像代数,没有空值和异常。但是你确实还会遇到异常,当你访问关闭的服务器或丢失的文件时。那你能做什么?

Option/Some/None

前面章节有说过:

def toInt(s: String): Option[Int] = {
    try {
        Some(Integer.parseInt(s.trim))
    } catch {
        case e: Exception => None
    }
}
toInt(x) match {
    case Some(i) => println(i)
    case None => println("That didn't work.")
}

val y = for {
    a <- toInt(stringA)
    b <- toInt(stringB)
    c <- toInt(stringC)
} yield a + b + c

Try/Success/Failure

Try/Success/Failure 使用起来与 Option/Some/None 很像,但还有两个很好的特性:

  • Try使得捕获异常很简单
  • Failure 包含异常信息
    使用前先导入:
import scala.util.{Try,Success,Failure}

更短了些:

def toInt(s: String): Try[Int] = Try {
    Integer.parseInt(s.trim)
}
def toInt(s: String): Try[Int] = Try(Integer.parseInt(s.trim))
scala> val a = toInt("1")
a: scala.util.Try[Int] = Success(1)
scala> val b = toInt("boo")
b: scala.util.Try[Int] = Failure(java.lang.NumberFormatException: For input string: "boo")

上面的两个都可以用来处理错误异常,一般,用Try/Success/Failure处理会抛出异常的代码,用Option/Some/None处理避免空值的问题。

Future

当你想用scala写并行和并发程序时,你可以使用原生的Java Thread ,但是用Scala 的Future来进行并发和并行程序的编写会更简单。

一个Future代表一个值,或许会或许不会 马上获取,但是会在某个时间点能够获取值,如果不能获取值会获取异常。

在单线程编程中,你把函数调用的结果绑定给一个变量:

def aShortRunningTask(): Int = 42
val x = aShortRunningTask

当使用Future时呢:

def aLongRunningTask(): Future[Int] = ???
val x = aLongRunningTask

aLongRunningTask 会消耗不确定的时间来返回值。

Future 时一次性的。“在其他线程上进行一个相对较慢的计算,等结果出来了再告诉我。”

一个Future的示例

当你有一个较慢的算法要计算时,这时像把他从主线程中拿掉,放到别处慢慢运行,让主线程能继续运行。

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
scala> val a = Future { Thread.sleep(10*1000); 42 }
a: scala.concurrent.Future[Int] = Future(<not completed>)
scala> val b = a.map(_ * 2)
b: scala.concurrent.Future[Int] = Future(<not completed>)
scala> b
res1: scala.concurrent.Future[Int] = Future(Success(84))

Future的值类型为Success或Failure:

a.onComplete {
    case Success(value) => println(s"Got the callback, value = $value")
    case Failure(e) => e.printStackTrace
}

多个Future配合

想象你要去服务器访问股票价格,这要消耗些时间,还可能访问不到:

def getStockPrice(stockSymbol: String): Future[Double] = ???

以随机值代表访问消耗时间:

def getStockPrice(stockSymbol: String): Future[Double] = Future {
    val r = scala.util.Random
    val randomSleepTime = r.nextInt(3000)
    val randomPrice = r.nextDouble * 1000
    sleep(randomSleepTime)
    randomPrice
}
现在你想同时获取三支股票的价格
package futures

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}

object MultipleFutures extends App {

    // use this to determine the “delta time” below
    val startTime = currentTime

    // (a) create three futures
    val aaplFuture = getStockPrice("AAPL")
    val amznFuture = getStockPrice("AMZN")
    val googFuture = getStockPrice("GOOG")

    // (b) get a combined result in a for-expression
    val result: Future[(Double, Double, Double)] = for {
        aapl <- aaplFuture
        amzn <- amznFuture
        goog <- googFuture
    } yield (aapl, amzn, goog)

    // (c) do whatever you need to do with the results
    result.onComplete {
        case Success(x) => {
            val totalTime = deltaTime(startTime)
            println(s"In Success case, time delta: ${totalTime}")
            println(s"The stock prices are: $x")
        }
        case Failure(e) => e.printStackTrace
    }

    // important for a short parallel demo: you need to keep
    // the jvm’s main thread alive
    sleep(5000)

    def sleep(time: Long): Unit = Thread.sleep(time)

    // a simulated web service
    def getStockPrice(stockSymbol: String): Future[Double] = Future {
        val r = scala.util.Random
        val randomSleepTime = r.nextInt(3000)
        println(s"For $stockSymbol, sleep time is $randomSleepTime")
        val randomPrice = r.nextDouble * 1000
        sleep(randomSleepTime)
        randomPrice
    }

    def currentTime = System.currentTimeMillis()
    def deltaTime(t0: Long) = currentTime - t0

}

Future还有很多其他方法,可以专门了解:

  • onComplete
  • onSuccess
  • onFailure
  • filter
  • foreach
  • map
  • andThen
  • fallbackTo
  • recoverWith
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值