Scala 类型
Scala中,所有的值都是类对象,而所有的类,包括值类型,都最终继承自一个统一的根类型Any。统一类型,是Scala的又一大特点。更特别的是,Scala中还定义了几个底层类(Bottom Class),比如Null和Nothing。
Null是所有引用类型的子类型,而Nothing是所有类型的子类型。Null类只有一个实例对象,null,类似于Java中的null引用。null可以赋值给任意引用类型,但是不能赋值给值类型。
Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。
Unit类型用来标识过程,也就是没有明确返回值的函数。 由此可见,Unit类似于Java里的void。Unit只有一个实例,(),这个实例也没有实质的意义。
Option Some None 设计目的 :避免使用null
大多数语言都有一个特殊的关键字或者对象来表示一个对象引用的是“无”,在Java,它是null。在Java 里,null 是一个关键字,不是一个对象,所以对它调用任何方法都是非法的。但是这对语言设计者来说是一件令人疑惑的选择。为什么要在程序员希望返回一个对象的时候返回一个关键字呢?
Scala鼓励在变量和函数返回值可能不会引用任何值的时候使用Option类型。在没有值的时候,使用None,这是Option的一个子类。如果有值可以引用,就使用Some来包含这个值。Some也是Option的子类。 None被声明为一个对象,而不是一个类,因为我们只需要它的一个实例。这样,它多少有点像null关键字,但它却是一个实实在在的,有方法的对象。
Option类型的值通常作为Scala集合类型(List,Map等)操作的返回类型。比如Map的get方法:
1 2 3 4 5 6 7 8 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
异常给你的。
我们也可以选用另外一个方法,getOrElse
。这个方法在这个Option是Some的实例时返回对应的值,而在是None的实例时返回传入的参数。换句话说,传入getOrElse
的参数实际上是默认返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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变量:
1 2 3 4 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)。
Tips4For 循环 如果我们把Option当成一般的List来用,并且用一个for循环来走访这个Option的时候,如果Option是None,那这个for循环里的程序代码自然不会执行,于是我们就达到了不用检查Option是否为None这件事 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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)
Tips4map操作 在函数式编程中有一个核心的概念之一是转换,所以大部份支持函数式编程语言,都支持一种叫map()的动作,这个动作是可以帮你把某个容器的内容,套上一些动作之后,变成另一个新的容器。 现在我们考虑如何用Option的map方法实现length: xxx
的输出形式:
先算出 Option 容器内字符串的长度 然后在长度前面加上 “length: “ 字样 最后把容器走访一次,印出容器内的东西
1 2 3 4 5 scala> value1.map(_.length).map("length: " + _).foreach(println) length: 6 scala> value1.map("length: " + _.length).foreach(println) length: 6
透过这样「转换」的方法,我们一样可以达成想要的效果,而且同样不用去做「是否为 None」的判断。
Scala Java Map Scala内部Map转换: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def main (args: Array [String ]): Unit = { val im: Map [Int , String ] = scala.collection.immutable.Map (1 ->"one" ,2 ->"two" ) val imh: HashMap [Int , String ] = scala.collection.immutable.HashMap (1 ->"one" ,2 ->"two" ) val mu: mutable.Map [Int , String ] = scala.collection.mutable.Map (1 ->"one" ,2 ->"two" ) println(im,manOf(im)) println(imh,manOf(imh)) println(mu,manOf(mu)) val mui: Map [Int , String ] = mu.toMap println(mui,manOf(mui)) val immu: mutable.Map [Int , String ] = scala.collection.mutable.Map (im.toSeq: _*) println(immu,manOf(immu)) val imhmu: mutable.Map [Int , String ] = scala.collection.mutable.Map (imh.toSeq: _*) println(imhmu,manOf(imhmu)) }
结果:
1 2 3 4 5 6 7 8 (Map(1 -> one, 2 -> two),scala.collection.immutable.Map[Int, java.lang.String]) (Map(1 -> one, 2 -> two),scala.collection.immutable.HashMap[Int, java.lang.String]) (Map(2 -> two, 1 -> one),scala.collection.mutable.Map[Int, java.lang.String]) (Map(2 -> two, 1 -> one),scala.collection.immutable.Map[Int, java.lang.String]) (Map(2 -> two, 1 -> one),scala.collection.mutable.Map[Int, java.lang.String]) (Map(2 -> two, 1 -> one),scala.collection.mutable.Map[Int, java.lang.String])
Java中 把Java Map 转化成 Scala Map 1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { HashMap<String, String> map = new HashMap<String, String>() { { put("1" , "2" ); } }; scala.collection.mutable.Map<String,String> mapTest = JavaConverters.mapAsScalaMapConverter(map).asScala(); Object objTest = Map$.MODULE$.<String,String>newBuilder().$plus$plus$eq(mapTest.toSeq()); Object resultTest = ((scala.collection.mutable.Builder) objTest).result(); scala.collection.immutable.Map<String,String> resultTest2 = (scala.collection.immutable.Map)resultTest; System.out.println(resultTest2.keySet()); }
稍微有点鸡肋,代码看不懂,笑
Scala中的双向转换 Scala提供了大量的方法来隐式转换所有主要的Java和Scala容器类型。其中提供了如下的双向类型转换:
1 2 3 4 5 6 7 8 Iterator <=> java.util.Iterator Iterator <=> java.util.Enumeration Iterable <=> java.lang.Iterable Iterable <=> java.util.Collection mutable.Buffer <=> java.util.List mutable.Set <=> java.util.Set mutable.Map <=> java.util.Map mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap
使用这些转换很简单,只需从JavaConverters对象中import它们即可。
1 2 scala> import collection.JavaConverters._ import collection.JavaConverters._
import之后,通过扩展方法 asScala 和 asJava 就可以在Scala容器和与之对应的Java容器之间进行隐式转换了
1 2 3 4 5 6 7 8 9 10 11 scala> import collection.mutable._ import collection.mutable._ scala> val jul: java.util.List[Int] = ArrayBuffer(1, 2, 3).asJava jul: java.util.List[Int] = [1, 2, 3] scala> val buf: Seq[Int] = jul.asScala buf: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2, 3) scala> val m: java.util.Map[String, Int] = HashMap("abc" -> 1, "hello" -> 2).asJava m: java.util.Map[String,Int] = {abc=1, hello=2}
还有一些Scala容器类型可以转换成对应的Java类型,但是并没有将相应的Java类型转换成Scala类型的能力,它们是:
1 2 3 4 Seq => java.util.List mutable.Seq => java.util.List Set => java.util.Set Map => java.util.Map
因为Java并未区分可变容器不可变容器类型,所以,虽然能将scala.immutable.List
转换成java.util.List
,但所有的修改操作都会抛出“UnsupportedOperationException”。参见下例:
1 2 3 4 5 6 scala> val jul = List(1, 2, 3).asJava jul: java.util.List[Int] = [1, 2, 3] scala> jul.add(7) java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:148)
Example 完整的Java Map => Scala mutbale Map => Scala Immutable Map => Option => 模式匹配得到结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import scala.collection.JavaConversions ._ val javaMap: util.Map [String , String ] = new util.HashMap [String , String ]() javaMap.put("1" ,"2" ) for (m <- javaMap) { println(m) } val scalaMuMap: mutable.Map [String , String ] = javaMap.asScala val scalaImmuMap: Predef .Map [String , String ] = scalaMuMap.toMap val maybeString: Option [String ] = scalaImmuMap.get("1" ) val show = (x: Option [String ]) => x match { case Some (str) => str case None => "?" } val str: String = show(maybeString) println(str) }
Scala mutable Map Test 1 2 3 4 5 6 7 8 9 val muMapTest: mutable.Map [String , Int ] = mutable.Map ("Alice" -> 10 , "Bob" -> 20 , "Kotlin" -> 30 )muMapTest("Alice" ) = 99 println(muMapTest("Alice" )) muMapTest += ("Bob" -> 98 ) println(muMapTest) muMapTest -= ("Alice" , "Kotlin" ) println(muMapTest) println(muMapTest.get("Bob" ))
More 4 Scala Map Action map1 ++ map2 (类似于Java Map的 putAll)
Scala 的 Seq Scala的 Seq将是Java的List,Scala 的 List将是Java的 LinkedList。
Seq是一个trait,它相当于Java的接口,但相当于即将到来的防御者方法。
Scala的List是一个抽象类,由Nil和::扩展,这是List的具体实现。
所以,在Java的List是一个接口,Scala的List是一个实现。
除此之外,Scala的List是不可变的,这不是LinkedList的情况。事实上,Java没有等价的不可变集合(只读的东西只保证新的对象不能改变,但你仍然可以改变旧的,因此,“只读”一个)。 Scala的List是由编译器和库高度优化的,它是函数式编程中的基本数据类型。然而,它有限制,它不足以并行编程。这些天,Vector是一个比List更好的选择,但习惯是很难打破。 Seq是一个很好的泛化序列,所以如果你编程到接口,你应该使用它。注意,实际上有三个:collection.Seq,collection.mutable.Seq和collection.immutable.Seq,它是后一个是“默认”导入到范围。 还有GenSeq和ParSeq。后面的方法在可能的情况下并行运行,前者是Seq和ParSeq的父代,这是当代码的并行性无关紧要的合适的泛化。它们都是相对新引入的,因此人们不会使用它们。
Scala match 基本介绍 模式匹配语法中,采用 match 关键字声明,每个分支采用 case 关键字进行声明,当需要匹配时, 会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下 一个分支进行判断。如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句。
Tips
如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句
如果所有 case 都不匹配,又没有写 case _ 分支,那么会抛出 MatchError
每个 case 中,不用 break 语句,自动中断 case
可以在 match 中使用其它类型,而不仅仅是字符
=> 等价于 java swtich 的 :
=> 后面的代码块到下一个 case, 是作为一个整体执行,可以使用{} 扩起来,也可以不扩
守卫 如果想要表达匹配某个范围的数据,那么就需要在模式匹配中增加守卫条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 object MatchIfDemo01 { def main (args: Array [String ]): Unit = { for (ch <- "+-3!" ) { var sign = 0 var digit = 0 ch match { case '+' if ch.toString.equals("3" ) => sign = 1 case '-' => sign = -1 case _ if ch.toString.equals("3" ) => digit = 3 case _ if (ch > 1110 || ch < 120 ) => println("ch > 10" ) case _ => sign = 2 } println(ch + " " + sign + " " + digit) } } }
上面这段代码值得一提的是ch是‘+’
的时候,第一次匹配会匹配上 第一个判断,因为没有匹配上,所以接下来会匹配第三个,没有匹配上,匹配第四个,匹配上了,直接跳出。
变量名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 val ch = 'U 'ch match { case '+' => println("ok~" ) case mychar => println("ok~" + mychar) case _ => println ("ok~~" ) } val ch1 = '+'val res1 = ch1 match { case '+' => { print("使用这种方式,case里的最后一行就会返回结果给变量res,这种方式好," ) ch1 + " hello " } case _ => println ("ok~~" ) } println("res=" + res1)
类型匹配 可以匹配 对象的任意类型,这样做避免了使用 isInstanceOf 和 asInstanceOf 方法
类型匹配注意事项
Map[String, Int] 和 Map[Int, String]是两种不同的类型,其它类推。
在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错.
1 2 3 4 5 6 7 val obj = 10 obj match { case a:Int => a case _ => "啥也不是" }
如果 case _ 出现在 match 中间,则表示隐藏变量名,即不使用,而不是表示默认匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 val a = 1 val obj1: Any = if (a == 1 ) 1 else if (a == 2 ) "2" else if (a == 3 ) BigInt (3 )else if (a == 4 ) Map ("aa" -> 1 )else if (a == 5 ) Map (1 -> "aa" )else if (a == 6 )Array (1 , 2 , 3 )else if (a == 7 )Array ("aa" , 1 )else if (a == 8 )Array ("aa" )val result = obj1 match { case a: Int => a case b: Map [String , Int ] => "对象是一个字符串-数字的 Map 集合" case c: Map [Int , String ] => "对象是一个数字-字符串的 Map 集合" case d: Array [String ] => d case e: Array [Int ] => "对象是一个数字数组" case _: BigInt => Int .MaxValue case _ => "啥也不是" } println(result)
匹配数组
Array(0) 匹配只有一个元素且为 0 的数组。
Array(x,y) 匹配数组有两个元素,并将两个元素赋值为 x 和 y。当然可以依次类推 Array(x,y,z) 匹配数组有 3 个元素的等等….
Array(0,_*) 匹配数组以 0 开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 val arrs: Array [Array [Int ]] =Array (Array (0 ), Array (1 , 0 ), Array (0 , 1 , 0 ), Array (1 , 1 , 0 ), Array (1 , 1 , 0 , 1 )) for (arr <- arrs ) { val result = arr match { case Array (0 ) => "0" case Array (x, y) => x + "=" + y case Array (0 , _*) => "以 0 开头和数组" case _ => "什么集合都不是" } println("result = " + result) } import scala.collection.mutable.ArrayBuffer val arrs2 = Array (Array (0 ),Array (1 , 0 ), Array (0 , 1 , 0 ), Array (1 , 1 , 0 ), Array (1 , 1 , 0 , 1 )) for (arr <- arrs2 ) { val result = arr match { case Array (x,y)=>ArrayBuffer (y,x) case _ => "不处理~~" } println(result) }
匹配列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 for (list <-Array (List (0 ), List (1 , 0 ), List (88 ), List (0 , 0 , 0 ), List (1 , 0 , 0 ))) { val result = list match { case 0 :: Nil => "0" case x :: y :: Nil => x + " " + y case 0 :: tail => "0 ..." case x :: Nil => x case _ => "something else" } println(result) println("==================================" ) val site: immutable.Seq [String ] = "Runoob" :: ("Google" :: ("Baidu" :: Nil )) val site2 = site site.foreach(println) val nums = 1 :: (2 :: (3 :: (4 :: Nil ))) val nums2: List [Int ] = nums nums.foreach(println) val dim = (1 :: (0 :: (0 :: Nil ))) :: (0 :: (1 :: (0 :: Nil ))) :: (0 :: (0 :: (1 :: Nil ))) :: Nil val dim2: List [List [Int ]] = dim dim.foreach(println) }
补充一部分列表操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 val reslist0: List [String ] = "A" ::"B" ::Nil val reslist1: List [String ] = "C" +:"D" +:Nil val reslist2: List [String ] = Nil :+"E" :+"F" println(reslist0) println(reslist1) println(reslist2) val reslist3 = reslist0 ++ reslist1 println(reslist3) val reslist4 = reslist0 ::: reslist1 println(reslist4) val reslist5 = reslist0 :: reslist1 println(reslist5)
::
该方法被称为cons,意为构造,向队列的头部追加数据,创造新的列表。用法为 x::list
,其中x
为加入到头部的元素,无论x
是列表与否,它都只将成为新生成列表的第一个元素,也就是说新生成的列表长度为list的长度+1(btw, x::list
等价于list.::(x)
)
:+
和+:
两者的区别在于:+
方法用于在尾部追加元素,+:
方法用于在头部追加元素,和::
很类似,但是::
可以用于pattern match ,而+:
则不行. 关于+:
和:+
,只要记住冒号永远靠近集合类型就OK了。
++
该方法用于连接两个集合,list1++list2
:::
该方法只能用于连接两个List类型的集合
匹配元组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (pair <- Array ((0 , 1 ), (1 , 0 ), (10 , 30 ), (1 , 1 ), (1 , 0 , 2 ))) { val result = pair match { case (0 , _) => "0 ..." case (y, 0 ) => y case (x, y) => (y, x) case _ => "other" } println(result) }
对象匹配 基本介绍 对象匹配,什么才算是匹配呢?,规则如下: 1) case 中对象的 unapply 方法(对象提取器)返回 Some 集合则为匹配成功 2) 返回 None 集合则为匹配失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 val number: Double = Square (5.0 ) number match { case Square (n) => println("匹配成功 n=" + n) case _ => println("nothing matched" ) } Object Square { def unapply (z:Double ): Option [Double ] = { println("unapple 被调用 z 是=" + z) Some (Math .sqrt(z)) } def apply (z:Double ):Double = z * z }
上面代码中的unapply方法就是模式匹配中调用的方法。这个例子可以深刻的理解模式匹配和unapply的关系。
对象匹配的特殊情况
当 case 后面的对象提取器方法的参数为多个,则会默认调用 def unapplySeq() 方法
如果 unapplySeq 返回是 Some,获取其中的值,判断得到的 sequence 中的元素的个数是否是三个 如果是三个,则把三个元素分别取出,赋值给 first,second 和 third
其它的规则不变.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 object MatchObjectDemo2 { def main (args: Array [String ]): Unit = { val namesString = "Alice,Bob,Thomas" namesString match { case Names (first, second, third) => { println("the string contains three people's names" ) println(s"$first $second $third " ) } case _ => println("nothing matched" ) } } } object Names { def unapplySeq (str: String ): Option [Seq [String ]] = { if (str.contains("," )) Some (str.split("," )) else None } }
变量声明中的模式 1 2 3 4 5 6 7 8 val (x,y,z) = (1 ,2 ,"hello" )println("x=" + x) val (q,r) = BigInt (10 ) /% 3 println(q,r) val arr = Array (1 ,7 ,2 ,9 )val Array (first,second, _*) = arr println(first,second)
for表达式中的模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 val map = Map ("A" -> 1 , "B" -> 0 , "C" -> 3 )for ((k, v) <- map) { println(k + " -> " + v) } println("--------------(k, 0) <- map-------------------" ) for ((k, 0 ) <- map) { println(k + " --> " + 0 ) } println("--------------(k, v) <- map if v == 0-------------------" ) for ((k, v) <- map if v >= 1 ) { println(k + " ---> " + v) }
样例类
样例类仍然是类
样例类用 case 关键字进行声明。
样例类是为 模式匹配而优化的类
构造器中的每一个参数都成为 val——除非它被显式地声明为 var(不建议这样做)
在样例类对应的伴生对象中提供 apply 方法让你不用 new 关键字就能构造出相应的对象
提供 unapply 方法让模式匹配可以工作
将自动生成 toString 、equals 、hashCode 和 和 copy 方法(有点类似模板类,直接给生成,供程序员使用) 8) 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 object CaseClassDemo02 { def main (args: Array [String ]): Unit = { for (amt <- Array (Dollar2 (1000.0 ), Currency2 (1000.0 , "RMB" ), NoAmount2 )) { val result = amt match { case Dollar2 (v) => "$" + v case Currency2 (v, u) => v + " " + u case NoAmount2 => "" } println(amt + ": " + result) } } } abstract class Amount2 case class Dollar2 (value: Double ) extends Amount2 //样例类 case class Currency2 (value: Double , unit: String ) extends Amount2 //样例类 case object NoAmount2 extends Amount2 //样例类
语句的中置表达式
1 2 3 4 List (1 ,3 ,5 ,9 ) match { case first :: second :: rest => println(first + " " + second + " " + rest.length + " " + rest) case _ => println("匹配不到..." ) }
结果:1 3 2 List(5, 9)
什么是中置表达式?1 + 2,这就是一个中置表达式。如果 unapply 方法产出一个元组,你可以在 case 语句中使用中置表示法。比如可以匹配一个 List 序列
密封类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 sealed abstract class Furniture case class Couch ( ) extends Furniture case class Chair ( ) extends Furniture class SealedDemo { def findPlaceTosit (furniture: Furniture ):String =furniture match { case a:Couch =>"lie on the couch" case b:Chair =>"site on the chair" } } object test { def main (args: Array [String ]): Unit = { val chair=Chair () val sea=new SealedDemo () println(sea.findPlaceTosit(chair)) } }
Match Example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 abstract class Item // 项 case class Book (description: String , price: Double ) extends Item //Bundle 捆 , discount : Double 折扣 , item: Item * ,可变参数 case class Bundle (description: String , discount: Double , item: Item * ) extends Item // 打折案例 120 val sale = Bundle ("书籍" ,10 ,Book ("漫画" ,40 ),Bundle ("文学作品" , 20 , Book ("《阳关》" , 80 ), Book ("《围城》" , 30 ))) val demo = sale match { case Bundle (_, _, Book (desc, _), _*) => desc } println(demo) val demo2 = sale match { case Bundle (_, _, art @ Book (_,_),rest @ _*) => (art, rest) } println(demo2) val demo3 = sale match { case Bundle (_,_,art3 @ Book (_, _),rest3) => (art3, rest3) } println(demo3) def price (it:Item ):Double = { it match { case Book (_,p) => p case Bundle (_,disc,its @ _*) => its.map(price).sum - disc } } println("price=" + price(sale))
模式匹配和if else 的区别 模式匹配其实本质上是提供一个方便的解构 (Destructuring) 数据结构的方式,以 scala 为例, pattern matching 其实用到了 scala 中提取器的功能, 提取器其实就是类中的 unapply () 方法。
其实 if else 只是 pattern matching 中的一个典型的用法,但并非它的全部。
同时, pattern matching 允许你解耦两个并不真正属于彼此的东西,也使得你的代码更易于测试。
apply() & unapply()
当Scala中类或者对象有一个主要用途的时候,apply方法就是很好的语法糖。
apply方法 => 类似 constructor4Java
unapply方法完全相反,unapply接受一个对象,从对象中提取出相应的值。
unapply方法主要用于模式匹配中。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Money (val value: Double , val country: String ) {}object Money { def apply (value: Double , country: String ) : Money = new Money (value, country) def unapply (money: Money ): Option [(Double , String )] = { if (money == null ) { None } else { Some (money.value, money.country) } } }
1 2 3 4 5 6 7 def testUnapply () = { val money = Money (10.1 , "RMB" ) money match { case Money (num, "RMB" ) => println("RMB: " + num) case _ => println("Not RMB!" ) } }
为什么只能在object对象定义main方法才能作为入口? 类比java中的main方法的修饰词public static得知main方法是static(静态的),在scala中静态方法或者属性是在object修改的对象中的,object修饰的对象是singleton,用于保存静态成员等。
break & continue 1 import scala.util.control.Breaks .{break , breakable}
break:
1 2 3 4 5 6 7 8 9 breakable( for (i<-0 until 10 ) { println(i) if (i==5 ){ break () } } )
continue:
1 2 3 4 5 6 7 8 9 for (i<-0 until 10 ){ breakable{ if (i==3 ||i==6 ) { break } println(i) } }