大多数语言都有一个特殊的关键字或者对象来表示一个对象引用的是“无”,在Java,它是null。在Java 里,null 是一个关键字,不是一个对象,所以对它调用任何方法都是非法的。但是这对语言设计者来说是一件令人疑惑的选择。为什么要在程序员希望返回一个对象的时候返回一个关键字呢?
为了让所有东西都是对象的目标更加一致,也为了遵循函数式编程的习惯,Scala鼓励你在变量和函数返回值可能不会引用任何值的时候使用Option类型。在没有值的时候,使用None,这是Option的一个子类。如果有值可以引用,就使用Some来包含这个值。Some也是Option的子类。
None被声明为一个对象,而不是一个类,因为我们只需要它的一个实例。这样,它多少有点像null关键字,但它却是一个实实在在的,有方法的对象。
Option类型的值通常作为Scala集合类型(List,Map等)操作的返回类型。比如Map的get方法:
package zpark1126.am
import scala.reflect.ClassTag
object TestOne {
def main(args: Array[String]): Unit = {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "China" -> "Beijing")
println("maybeString1 :" + maybeString1) //Some(Paris)
val maybeString2: Option[String] = capitals.get("USA")
println("maybeString2 :" + maybeString2) //None
}
}
返回值类型为:Option[String],即:"Paris"或者是None
scala> val capitals = Map("France"->"Paris", "Japan"->"Tokyo", "China"->"Beijing")
capitals: scala.collection.immutable.Map[String,String] = Map(France -> Paris, Japan -> Tokyo, China -> Beijing)
scala> capitals get "France"
res0: Option[String] = Some(Paris)
scala> capitals get "North Pole"
res1: Option[String] = None
Option有两个子类别,Some和None。当程序回传Some的时候,代表这个函式成功地给了你一个String,而你可以透过get()函数拿到那个String,如果程序返回的是None,则代表没有字符串可以给你。
在返回None,也就是没有String给你的时候,如果你还硬要调用get()来取得 String 的话,Scala一样是会抛出一个NoSuchElementException异常给你的。
package zpark1126.am
import scala.reflect.ClassTag
object TestOne {
def main(args: Array[String]): Unit = {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "China" -> "Beijing")
val maybeString1: Option[String] = capitals.get("France")
println("maybeString1 :" + maybeString1.get) //Paris
val maybeString2: Option[String] = capitals.get("USA")
println(maybeString2.get)
/**
* Exception in thread "main" java.util.NoSuchElementException: None.get
* at scala.None$.get(Option.scala:347)
* at scala.None$.get(Option.scala:345)
* at zpark1126.am.TestOne$.main(TestOne.scala:13)
* at zpark1126.am.TestOne.main(TestOne.scala)
*/
val value1: String = maybeString1.get
println("value1 :" + value1) //Paris
val value2: String = maybeString2.get
println(value2)
/**
* Exception in thread "main" java.util.NoSuchElementException: None.get
* at scala.None$.get(Option.scala:347)
* at scala.None$.get(Option.scala:345)
* at zpark1126.am.TestOne$.main(TestOne.scala:23)
* at zpark1126.am.TestOne.main(TestOne.scala)
*/
}
}
我们也可以选用另外一个方法,getOrElse。这个方法在这个Option是Some的实例时返回对应的值,而在是None的实例时返回传入的参数。换句话说,传入getOrElse的参数实际上是默认返回值。
package zpark1126.am
import scala.reflect.ClassTag
object TestOne {
def main(args: Array[String]): Unit = {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "China" -> "Beijing")
val maybeString1: Option[String] = capitals.get("France")
val maybeString2: Option[String] = capitals.get("USA")
val value2: String = maybeString1.get
println("value2 :" + value2) //3333333: Paris
val str: String = maybeString2.getOrElse("oops")
println("str :" + str) //oops
}
}
通过模式匹配分离可选值,如果匹配的值是Some的话,将Some里的值抽出赋给x变量:
package zpark1126.am
import scala.reflect.ClassTag
object TestOne {
def main(args: Array[String]): Unit = {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "China" -> "Beijing")
val maybeString1: Option[String] = capitals.get("France")
val maybeString2: Option[String] = capitals.get("USA")
val str1: String = showCapital(maybeString1)
println("str1 :" + str1)//str1 :Paris
val str2: String = showCapital(maybeString2)
println("str2 :" + str2)//str2 :?
}
def showCapital(x:Option[String]) = x match {
case Some(s) => s
case None => "?"
}
}
在Scala里Option[T]实际上是一个容器,就像数组或是List一样,你可以把他看成是一个可能有零到一个元素的List。
当你的Option里面有东西的时候,这个List的长度是1(也就是 Some),而当你的Option里没有东西的时候,它的长度是0(也就是 None)。
for循环
如果我们把Option当成一般的List来用,并且用一个for循环来走访这个Option的时候,如果Option是None,那这个for循环里的程序代码自然不会执行,于是我们就达到了不用检查Option是否为None这件事
scala> val map1 = Map("key1" -> "value1")
map1: scala.collection.immutable.Map[String,String] = Map(key1 -> value1)
scala> val value1 = map1.get("key1")
value1: Option[String] = Some(value1)
scala> val value2 = map1.get("key2")
value2: Option[String] = None
scala> def printContentLength(x: Option[String]) {
| for (c <- x){
| println(c.length)
| }
| }
printContentLength: (x: Option[String])Unit
scala> printContentLength(value1)
6
scala> printContentLength(value2)
长度:
package zpark1126.am
import scala.reflect.ClassTag
object TestOne {
def main(args: Array[String]): Unit = {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "China" -> "Beijing")
val maybeString1: Option[String] = capitals.get("France")
val maybeString2: Option[String] = capitals.get("USA")
showContentLen(maybeString1) //长度为 :5
showContentLen(maybeString2)
}
def showContentLen(x:Option[String]) = {
for(c <- x){
println("长度为 :" + c.length)
}
}
}
map操作
在函数式编程中有一个核心的概念之一是转换,所以大部份支持函数式编程语言,都支持一种叫map()的动作,这个动作是可以帮你把某个容器的内容,套上一些动作之后,变成另一个新的容器。
现在我们考虑如何用Option的map方法实现length: xxx的输出形式:
scala> value1.map(_.length).map("length: " + _).foreach(println)
length: 6
scala> value1.map("length: " + _.length).foreach(println)
length: 6
package zpark1126.am
import scala.reflect.ClassTag
object TestOne {
def main(args: Array[String]): Unit = {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "China" -> "Beijing")
val maybeString1: Option[String] = capitals.get("France")
val maybeString2: Option[String] = capitals.get("USA")
val maybeString: Option[String] = maybeString1.map(_.length).map("length :" + _)
println("maybeString :" + maybeString)//maybeString :Some(length :5)
maybeString.foreach(println)//length :5
}
def showContentLen(x:Option[String]) = {
for(c <- x){
println("长度为 :" + c.length)
}
}
}
************************************************************************************************************************************************
package zpark1126.am
import scala.reflect.ClassTag
object TestOne {
def main(args: Array[String]): Unit = {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo", "China" -> "Beijing")
val maybeString1: Option[String] = capitals.get("France")
println("maybeString1 :" + maybeString1) //Some(Paris)
println("maybeString1 :" + maybeString1.get) //Paris
val maybeString2: Option[String] = capitals.get("USA")
println("maybeString2 :" + maybeString2) //None
// println(maybeString2.get)
/**
* Exception in thread "main" java.util.NoSuchElementException: None.get
* at scala.None$.get(Option.scala:347)
* at scala.None$.get(Option.scala:345)
* at zpark1126.am.TestOne$.main(TestOne.scala:13)
* at zpark1126.am.TestOne.main(TestOne.scala)
*/
val value1: String = maybeString1.get
println("value1 :" + value1) //Paris
// val value2: String = maybeString2.get
// println(value2)
/**
* Exception in thread "main" java.util.NoSuchElementException: None.get
* at scala.None$.get(Option.scala:347)
* at scala.None$.get(Option.scala:345)
* at zpark1126.am.TestOne$.main(TestOne.scala:23)
* at zpark1126.am.TestOne.main(TestOne.scala)
*/
val value2: String = maybeString1.get
println("value2 :" + value2) //3333333: Paris
val str: String = maybeString2.getOrElse("oops")
println("str :" + str) //oops
val str1: String = showCapital(maybeString1)
println("str1 :" + str1)//str1 :Paris
val str2: String = showCapital(maybeString2)
println("str2 :" + str2)//str2 :?
showContentLen(maybeString1)//长度为 :5
showContentLen(maybeString2)
val maybeString: Option[String] = maybeString1.map(_.length).map("length :" + _)
println("maybeString :" + maybeString)//maybeString :Some(length :5)
maybeString.foreach(println)//length :5
}
def showCapital(x:Option[String]) = x match {
case Some(s) => s
case None => "?"
}
def showContentLen(x:Option[String]) = {
for(c <- x){
println("长度为 :" + c.length)
}
}
}