Scala中的Option

Scala中的Option

避免null使用

大多数语言都有一个特殊的关键字或者对象来表示一个对象引用的是“无”,在Java,它是null

写Java程序的时候,经常会碰到没有有意义的东西可以返回,我们返回null。

在Java 里,null 是一个关键字,不是一个对象,所以对它调用任何方法都是非法的。因此调用方必须检查返回值,不然会有NullPointerException的异常。

这逼迫我们去check函数的返回值,或者捕获异常。这都不是明智的选择。

同时还有一个例子

Map<String, String> map = ???
String valFor2014 = map.get(“1024”); // null

if (valFor1024 == null)
    abadon();
else doSomething();

null到底代表key找不到还是说1024对应的值就是null?在有些时候,会产生具有二义性的代码。

Scala的Option类型

为了让所有东西都是对象的目标更加一致,也为了遵循函数式编程的习惯,Scala鼓励你在变量和函数返回值可能不会引用任何值的时候使用Option类型。

  • Option可以看作是一个容器,容器的size是1或0;
  • size为1的时候,即有值可以引用,就是一个Some[A](x: A),如Some[String]
  • size为0的时候,即没有值的情况下就是一个None,它是Option的一个子类。None被声明为一个对象,而不是一个类,因为我们只需要它的一个实例。这样,它多少有点像null关键字,但它却是一个实实在在的,有方法的对象。

应用例子

Option类型的值通常作为Scala集合类型(List,Map等)操作的返回类型。比如Map的get方法:

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

当程序回传Some的时候,代表这个函式成功地给了你一个String,而你可以透过get()函数拿到那个String,如果程序返回的是None,则代表没有字符串可以给你。

在返回None,也就是没有String给你的时候,如果你还硬要调用get()来取得 String 的话,Scala一样是会抛出一个NoSuchElementException异常给你的。

我们也可以选用另外一个方法,getOrElse。这个方法在这个Option是Some的实例时返回对应的值,而在是None的实例时返回传入的参数。换句话说,传入getOrElse的参数实际上是默认返回值。

scala> capitals get "North Pole" get
warning: there was one feature warning; re-run with -feature for details
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  ... 33 elided

scala> capitals get "France" get
warning: there was one feature warning; re-run with -feature for details
res3: String = Paris

scala> (capitals get "North Pole") getOrElse "Oops"
res7: String = Oops

scala> capitals get "France" getOrElse "Oops"
res8: String = Paris

通过模式匹配分离可选值,如果匹配的值是Some的话,将Some里的值抽出赋给x变量:

def showCapital(x: Option[String]) = x match {
    case Some(s) => s
    case None => "?"
}

配合其他容器

val a: Seq[String] =
  Seq("1", "2", "3", null, "4")
val b: Seq[Option[String]] =
  Seq(Some("1"), Some("2"), Some("3"), None, Some("4"))

a.filter(_ != null).map(_.toInt)
//res0: Seq[Int] = List(1, 2, 3, 4)
//如果你忘了检查,编译器是看不出来的,只能在跑崩的时候抛异常

b.flatMap(_.map(_.toInt))
//res1: Seq[Int] = List(1, 2, 3, 4)

Seq(1,2,3).headOption
//res2: Option[Int] = Some(1)

Seq(1,2,3).find(_ == 5)
//res3: Option[Int] = None

Seq(1,2,3).lastOption
//res4: Option[Int] = Some(3)

Vector(1,2,3).reduceLeft(_ + _)
//res5: Int = 6

Vector(1,2,3).reduceLeftOption(_ + _)
//res6: Option[Int] = Some(6)
//在vector为空的时候也能用

Seq("a", "b", "c", null, "d").map(Option(_))
//res7: Seq[Option[String]] =
// List(Some(a), Some(b), Some(c), None, Some(d))
//原始数据转换成option也很方便

实际上,多亏Scala的静态类型,你并不能错误地尝试在一个可能为null的值上调用方法。虽然在Java中这是个很容易犯的错误,它在Scala却通不过编译,这是因为Java中没有检查变量是否为null的编程作为变成Scala中的类型错误(不能将Option[String]当做String来使用)。所以,Option的使用极强地鼓励了更加弹性的编程习惯。

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)

容器操作

scala> val a: Option[String] = Some("1024")
a: Option[String] = Some(1024)

scala> val b: Option[String] = None
b: Option[String] = None

scala> a.map(_.toInt)
res0: Option[Int] = Some(1024)

scala> b.map(_.toInt)
res1: Option[Int] = None
//res1: Option[Int] = None,不会甩exception

scala> a.filter(_ == "2048")
res2: Option[String] = None

scala> b.filter(_ == "2048")
res7: Option[String] = None

scala> a.getOrElse("2048")
res8: String = 1024

scala> b.getOrElse("2048")
res9: String = 2048

scala> a.map(_.toInt).map(_ + 1).map(_ / 5).map(_ / 2 == 0)
res10: Option[Boolean] = Some(false)

Scala中的Option

避免null使用

大多数语言都有一个特殊的关键字或者对象来表示一个对象引用的是“无”,在Java,它是null

写Java程序的时候,经常会碰到没有有意义的东西可以返回,我们返回null。

在Java 里,null 是一个关键字,不是一个对象,所以对它调用任何方法都是非法的。因此调用方必须检查返回值,不然会有NullPointerException的异常。

这逼迫我们去check函数的返回值,或者捕获异常。这都不是明智的选择。

同时还有一个例子

Map<String, String> map = ???
String valFor2014 = map.get(“1024”); // null

if (valFor1024 == null)
    abadon();
else doSomething();

null到底代表key找不到还是说1024对应的值就是null?在有些时候,会产生具有二义性的代码。

Scala的Option类型

为了让所有东西都是对象的目标更加一致,也为了遵循函数式编程的习惯,Scala鼓励你在变量和函数返回值可能不会引用任何值的时候使用Option类型。

  • Option可以看作是一个容器,容器的size是1或0;
  • size为1的时候,即有值可以引用,就是一个Some[A](x: A),如Some[String]
  • size为0的时候,即没有值的情况下就是一个None,它是Option的一个子类。None被声明为一个对象,而不是一个类,因为我们只需要它的一个实例。这样,它多少有点像null关键字,但它却是一个实实在在的,有方法的对象。

应用例子

Option类型的值通常作为Scala集合类型(List,Map等)操作的返回类型。比如Map的get方法:

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

当程序回传Some的时候,代表这个函式成功地给了你一个String,而你可以透过get()函数拿到那个String,如果程序返回的是None,则代表没有字符串可以给你。

在返回None,也就是没有String给你的时候,如果你还硬要调用get()来取得 String 的话,Scala一样是会抛出一个NoSuchElementException异常给你的。

我们也可以选用另外一个方法,getOrElse。这个方法在这个Option是Some的实例时返回对应的值,而在是None的实例时返回传入的参数。换句话说,传入getOrElse的参数实际上是默认返回值。

scala> capitals get "North Pole" get
warning: there was one feature warning; re-run with -feature for details
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  ... 33 elided

scala> capitals get "France" get
warning: there was one feature warning; re-run with -feature for details
res3: String = Paris

scala> (capitals get "North Pole") getOrElse "Oops"
res7: String = Oops

scala> capitals get "France" getOrElse "Oops"
res8: String = Paris

通过模式匹配分离可选值,如果匹配的值是Some的话,将Some里的值抽出赋给x变量:

def showCapital(x: Option[String]) = x match {
    case Some(s) => s
    case None => "?"
}

配合其他容器

val a: Seq[String] =
  Seq("1", "2", "3", null, "4")
val b: Seq[Option[String]] =
  Seq(Some("1"), Some("2"), Some("3"), None, Some("4"))

a.filter(_ != null).map(_.toInt)
//res0: Seq[Int] = List(1, 2, 3, 4)
//如果你忘了检查,编译器是看不出来的,只能在跑崩的时候抛异常

b.flatMap(_.map(_.toInt))
//res1: Seq[Int] = List(1, 2, 3, 4)

Seq(1,2,3).headOption
//res2: Option[Int] = Some(1)

Seq(1,2,3).find(_ == 5)
//res3: Option[Int] = None

Seq(1,2,3).lastOption
//res4: Option[Int] = Some(3)

Vector(1,2,3).reduceLeft(_ + _)
//res5: Int = 6

Vector(1,2,3).reduceLeftOption(_ + _)
//res6: Option[Int] = Some(6)
//在vector为空的时候也能用

Seq("a", "b", "c", null, "d").map(Option(_))
//res7: Seq[Option[String]] =
// List(Some(a), Some(b), Some(c), None, Some(d))
//原始数据转换成option也很方便

实际上,多亏Scala的静态类型,你并不能错误地尝试在一个可能为null的值上调用方法。虽然在Java中这是个很容易犯的错误,它在Scala却通不过编译,这是因为Java中没有检查变量是否为null的编程作为变成Scala中的类型错误(不能将Option[String]当做String来使用)。所以,Option的使用极强地鼓励了更加弹性的编程习惯。

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)

容器操作

scala> val a: Option[String] = Some("1024")
a: Option[String] = Some(1024)

scala> val b: Option[String] = None
b: Option[String] = None

scala> a.map(_.toInt)
res0: Option[Int] = Some(1024)

scala> b.map(_.toInt)
res1: Option[Int] = None
//res1: Option[Int] = None,不会甩exception

scala> a.filter(_ == "2048")
res2: Option[String] = None

scala> b.filter(_ == "2048")
res7: Option[String] = None

scala> a.getOrElse("2048")
res8: String = 1024

scala> b.getOrElse("2048")
res9: String = 2048

scala> a.map(_.toInt).map(_ + 1).map(_ / 5).map(_ / 2 == 0)
res10: Option[Boolean] = Some(false)

猜你喜欢

转载自blog.csdn.net/jenrey/article/details/81010664
今日推荐