第七章 模式匹配
Scala中的模式匹配类似与Java中的switch语法,但是scala从语法中补充了更多的功能,所以更加强大。
7.1 基本语法
模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。
val student1 = Student1("alice", 18)
val student2 = Student1("alice", 19)
val student3 = Student1("bob", 18)
val result = student1 match {
case Student1("alice", 18) => "Yes, this is alice 18"
case _ => "No"
}
println(result)
说明
(1)如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句,若此时没有case _ 分支,那么会抛出MatchError。
(2)每个case中,不需要使用break语句,自动中断case。
(3)match case语句可以匹配任何类型,而不只是字面量。
(4)=> 后面的代码块,直到下一个case语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。
7.2 模式守卫
1)说明
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
2)案例实操
def abs(num: Int): Int = {
num match {
case i if i > 0 => i
case i if i < 0 => -i
case _ => 0
}
}
println(abs(8))
println(abs(-235))
println(abs(0))
7.3 模式匹配类型
7.3.1 匹配常量
1)说明
Scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
2)案例实操、
def describeConst(x: Any): Any = x match {
case 10 => "Int Ten"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
case _ =>
}
println(describeConst(10))
println(describeConst("hello"))
println(describeConst(true))
println(describeConst("abc"))
7.3.2 匹配类型
1)说明
需要进行类型判断时,可以使用前文所学的isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。
2)案例实操
def describeType(x: Any): String = x match {
case i: Int => "Int " + i
case s: String => "String " + s
case b: Boolean => "Boolean " + b
case l: List[String] => "List " + l // 泛型擦除
case arr: Array[Int] => "Array[Int] " + arr.mkString(",")
case _ => "something else"
}
println(describeType(10))
println(describeType("hello"))
println(describeType(true))
println(describeType("abc"))
println(describeType(List()))
println(describeType(List("abc", "def")))
println(describeType(List(2,3,4,6)))
println(describeType(Array(2,5,2,65)))
println(describeType(Array("hello", "world")))
7.3.3 匹配数组
1)说明
scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组。
2)案例实操
val arrList: List[Any] = List(
Array(0),
Array(0, 1),
Array(1, 0),
Array(0, 1, 0),
Array(1, 1, 0),
Array(35, 47, 96, 45),
Array("hello", "world")
)
def describeArray(arr: Any): String = arr match {
case Array(0) => "Array(0)"
case Array(x, y) => "Array 2: " + x + " " + y
case Array(0, _*) => "Array with 0 start"
case Array(_, 1, _) => "Array 3 with 1 in middle"
case _ => "something else"
}
for( arr <- arrList ) println(describeArray(arr))
println(describeArray(Array('c', 'a')))
7.3.4 匹配列表
for( list <- List(
List(0),List(0, 1),List(1, 0),List(0, 1, 0),
List(1, 1, 0),List(35, 47, 96, 45),List("hello"),
List('a', 'b'),Map(('a', 1)) ) ){
val description: String = list match {
case List(0) => "List(0)"
case List(_, _) => "List 2 elements"
case List(0, _*) => "List with 0 start"
case List(a) => "List " + a
case _ => "something else"
}
println(description)
}
7.3.5 匹配元组
for( tuple <- List(
(0, 0),
(0, 1),
(1, 0),
(1, 1),
(1, 0, 2),
("hello", 1, 0.5)
) ){
val result = tuple match {
case (0, _) => "0, _"
case (y, 0) => y + " ,0"
case (a, b) => "二元组"
case (_, 1, a) => "_, 1, " + a
case _ => "其它"
}
println(result)
}
7.3.6 匹配对象和样例类
匹配对象案例
val student1 = new Student("alice", 18)
val student2 = new Student("alice", 19)
val student3 = new Student("bob", 18)
val result = student3 match {
case Student("alice", 18) => "Yes, this is alice 18"
case _ => "No"
}
println(result)
}
}
class Student(val name: String, val age: Int)
// 定义伴生对象
object Student {
def apply(name: String, age: Int): Student = new Student(name, age)
def unapply(student: Student): Option[(String, Int)] = {
if( student == null )
None
else
Some( (student.name, student.age) )
}
}
匹配样例类案例
val student1 = Student1("alice", 18)
val student2 = Student1("alice", 19)
val student3 = Student1("bob", 18)
val result = student1 match {
case Student1("alice", 18) => "Yes, this is alice 18"
case _ => "No"
}
println(result)
}
}
case class Student1( name: String, age: Int )
第八章 异常
8.1 Java异常处理
(1)Java语言按照try—catch—finally的方式来处理异常
(2)不管有没有异常捕获,都会执行finally,因此通常可以在finally代码块中释放资源。
(3)可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。
8.2 Scala异常处理
1)我们将可疑代码封装在try块中。在try块之后使用了一个catch处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止。
2)Scala的异常的工作机制和Java一样,但是Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。
3)异常捕捉的机制与其他语言中一样,如果有异常发生,catch子句是按次序捕捉的。因此,在catch子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在Scala中也不会报错,但这样是非常不好的编程风格。
4)finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样。
5)用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方
6)java提供了throws关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在try-catch块中,以避免程序异常终止。在Scala中,可以使用throws注解来声明异常
案例
object Test01_Exception {
def main(args: Array[String]): Unit = {
try{
val n = 1 / 0
} catch {
case e: ArithmeticException => println("算术异常")
case e: RuntimeException => println("其它异常")
case _ =>
} finally {
println("异常处理结束")
}
println("异常处理外的逻辑")
}
}
第九章 隐式转换
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译
9.1 隐式函数
1)说明
隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。
2)案例实操
需求:通过隐式转化为Int类型增加方法。
class MyRichInt(val self: Int) {
def myMax(i: Int): Int = {
if (self < i) i else self
}
def myMin(i: Int): Int = {
if (self < i) self else i
}
}
object TestImplicitFunction {
// 使用implicit关键字声明的函数称之为隐式函数
implicit def convert(arg: Int): MyRichInt = {
new MyRichInt(arg)
}
def main(args: Array[String]): Unit = {
// 当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换。也称之为自动转换
println(2.myMax(6))
}
}
9.2 隐式参数
普通方法或者函数中的参数可以通过implicit关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
1)说明
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数
2)案例实操
implicit val str: String = "hello world"
// implicit val str2: String = "hello world 2"
def sayHello(name: String)(implicit arg: String = "good bye"): Unit ={
println(name + " " + arg)
}
sayHello("alice")
}
9.3 隐式类
在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。
1)隐式类说明
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。
2)案例实操
object TestImplicitClass {
implicit class MyRichInt(arg: Int) {
def myMax(i: Int): Int = {
if (arg < i) i else arg
}
def myMin(i: Int) = {
if (arg < i) arg else i
}
}
def main(args: Array[String]): Unit = {
println(1.myMax(3))
}
}
第十章 泛型
10.1 协变和逆变
1)语法
class MyList[**+T**]{ //协变
}
class MyList[**-T**]{ //逆变
}
class MyList[T] //不变
2)说明
协变:Son是Father的子类,则MyList[Son] 也作为MyList[Father]的“子类”。
逆变:Son是Father的子类,则MyList[Son]作为MyList[Father]的“父类”。
不变:Son是Father的子类,则MyList[Father]与MyList[Son]“无父子关系”。
3)实操
//泛型模板
//class MyList<T>{}
//不变
//class MyList[T]{}
//协变
//class MyList[+T]{}
//逆变
//class MyList[-T]{}
class Parent{}
class Child extends Parent{}
class SubChild extends Child{}
object Scala_TestGeneric {
def main(args: Array[String]): Unit = {
//var s:MyList[Child] = new MyList[SubChild]
}
}
10.2 泛型上下限
1)语法
Class PersonList[T <: Person]{ //泛型上限
}
Class PersonList[T >: Person]{ //泛型下限
}
2)说明
泛型的上下限的作用是对传入的泛型进行限定。
3)实操
class Parent{}
class Child extends Parent{}
class SubChild extends Child{}
object Scala_TestGeneric {
def main(args: Array[String]): Unit = {
//test(classOf[SubChild])
//test[Child](new SubChild)
}
//泛型通配符之上限
//def test[A <: Child](a:Class[A]): Unit ={
// println(a)
//}
//泛型通配符之下限
//def test[A >: Child](a:Class[A]): Unit ={
// println(a)
//}
//泛型通配符之下限 形式扩展
def test[A >: Child](a:A): Unit ={
println(a.getClass.getName)
}
}
10.3 上下文限定
1)语法
def f[A : B](a: A) = println(a) //等同于def f[A](a:A)(implicit arg:B[A])=println(a)
2)说明
上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过implicitly[Ordering[A]]获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。
implicit val x = 1
val y = implicitly[Int]
val z = implicitly[Double]
3)实操
def f[A:Ordering](a:A,b:A) =implicitly[Ordering[A]].compare(a,b)
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)