1.模式匹配
基本语法
Scala 中的模式匹配类似于 Java 中的 switch ,但是 scala 从语法中补充了更多的功能,所以更加强大。
java中如下:
int i = 10
switch (i) {
case 10 :
System.out.println("10");
break;
case 20 :
System.out.println("20");
break;
default :
System.out.println("other number");
break;
}
模式匹配语法中,采用match关键字声明,每个分支采用 case 关键字进行声明,当需
要匹配时,会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹
配不成功,继续执行下一个分支进行判断。如果所有 case 都不匹配,那么会执行 ==case _==分支,
类似于 Java 中 default 语句。
模式守卫
说明
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
object Test01_PatternMatchBase {
def main(args: Array[String]): Unit = {
// 1. 基本定义语法
val x: Int = 5
val y: String = x match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "other"
}
println(y)
// 2. 示例:用模式匹配实现简单二元运算
val a = 25
val b = 13
def matchDualOp(op: Char): Int = op match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case '%' => a % b
case _ => -1
}
println(matchDualOp('+'))
println(matchDualOp('/'))
println(matchDualOp('\\'))
println("=========================")
// 3. 模式守卫
// 求一个整数的绝对值
def abs(num: Int): Int = {
num match {
case i if i >= 0 => i
case i if i < 0 => -i
}
}
println(abs(67))
println(abs(0))
println(abs(-24))
}
}
模式匹配类型
匹配常量
说明
Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
需要进行类型判断时,可以使用前文所学的 isInstanceOf[T]和 asInstanceOf[T]
scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素
为 0 的数组。
object Test02_MatchTypes {
def main(args: Array[String]): Unit = {
// 1. 匹配常量
def describeConst(x: Any): String = x match {
case 1 => "Int one"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
case _ => ""
}
println(describeConst("hello"))
println(describeConst('+'))
println(describeConst(0.3))
println("==================================")
// 2. 匹配类型
def describeType(x: Any): String = x match {
case i: Int => "Int " + i
case s: String => "String " + s
case list: List[String] => "List " + list
case array: Array[Int] => "Array[Int] " + array.mkString(",")
case a => "Something else: " + a
}
println(describeType(35))
println(describeType("hello"))
println(describeType(List("hi", "hello")))
println(describeType(List(2, 23)))
println(describeType(Array("hi", "hello")))
println(describeType(Array(2, 23)))
// 3. 匹配数组
for (arr <- List(
Array(0),
Array(1, 0),
Array(0, 1, 0),
Array(1, 1, 0),
Array(2, 3, 7, 15),
Array("hello", 1, 30),
)) {
val result = arr match {
case Array(0) => "0"
case Array(1, 0) => "Array(1, 0)"
case Array(x, y) => "Array: " + x + ", " + y // 匹配两元素数组
case Array(0, _*) => "以0开头的数组"
case Array(x, 1, z) => "中间为1的三元素数组"
case _ => "something else"
}
println(result)
}
println("=========================")
// 4. 匹配列表
// 方式一
for (list <- List(
List(0),
List(1, 0),
List(0, 0, 0),
List(1, 1, 0),
List(88),
List("hello")
)) {
val result = list match {
case List(0) => "0"
case List(x, y) => "List(x, y): " + x + ", " + y
case List(0, _*) => "List(0, ...)"
case List(a) => "List(a): " + a
case _ => "something else"
}
println(result)
}
// 方式二
val list1 = List(1, 2, 5, 7, 24)
val list = List(24)
list match {
case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest")
case _ => println("something else")
}
println("===========================")
// 5. 匹配元组
for (tuple <- List(
(0, 1),
(0, 0),
(0, 1, 0),
(0, 1, 1),
(1, 23, 56),
("hello", true, 0.5)
)){
val result = tuple match {
case (a, b) => "" + a + ", " + b
case (0, _) => "(0, _)"
case (a, 1, _) => "(a, 1, _) " + a
case (x, y, z) => "(x, y, z) " + x + " " + y + " " + z
case _ => "something else"
}
println(result)
}
}
}
变量声明中的模式
for 表达式中的模式
object Test03_MatchTupleExtend {
def main(args: Array[String]): Unit = {
// 1. 在变量声明时匹配
val (x, y) = (10, "hello")
println(s"x: $x, y: $y")
val List(first, second, _*) = List(23, 15, 9, 78)
println(s"first: $first, second: $second")
val fir :: sec :: rest = List(23, 15 , 9, 78)
println(s"first: $fir, second: $sec, rest: $rest")
println("=====================")
// 2. for推导式中进行模式匹配
val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13))
// 2.1 原本的遍历方式
for (elem <- list){
println(elem._1 + " " + elem._2)
}
// 2.2 将List的元素直接定义为元组,对变量赋值
for ((word, count) <- list ){
println(word + ": " + count)
}
println("-----------------------")
// 2.3 可以不考虑某个位置的变量,只遍历key或者value
for ((word, _) <- list)
println(word)
println("-----------------------")
// 2.4 可以指定某个位置的值必须是多少
for (("a", count) <- list){
println(count)
}
}
}
匹配对象
object Test04_MatchObject {
def main(args: Array[String]): Unit = {
val student = new Student("alice", 19)
// 针对对象实例的内容进行匹配
val result = student match {
case Student("alice", 18) => "Alice, 18"
case _ => "Else"
}
println(result)
}
}
// 定义类
class Student(val name: String, val age: Int)
// 定义伴生对象
object Student {
def apply(name: String, age: Int): Student = new Student(name, age)
// 必须实现一个unapply方法,用来对对象属性进行拆解
def unapply(student: Student): Option[(String, Int)] = {
if (student == null){
None
} else {
Some((student.name, student.age))
}
}
Student(“alice”, 18)该语句在执行时,实际调用的是 User 伴生对象中的
apply 方法,因此不用 new 关键字就能构造出相应的对象。
当将 Student(“alice”, 18)写在 case 后时[case Student(“alice”, 18) => “Alice, 18”],会默
认调用 unapply 方法(对象提取器),student作为 unapply 方法的参数,unapply 方法
将 user 对象的 name 和 age 属性提取出来,与 Student(“alice”, 18)中的属性值进行
匹配
case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功,
属性不一致,或返回 None,则匹配失败。
样例类
语法
case class Person (name: String, age: Int)
说明
○1 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中
自动提供了一些常用的方法,如 apply、unapply、toString、equals、hashCode 和 copy。
○2 样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例
类可以直接使用模式匹配,而无需自己实现 unapply 方法。
○3 构造器中的每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)
object Test05_MatchCaseClass {
def main(args: Array[String]): Unit = {
val student = Student1("alice", 18)
// 针对对象实例的内容进行匹配
val result = student match {
case Student1("alice", 18) => "Alice, 18"
case _ => "Else"
}
println(result)
}
}
// 定义样例类
case class Student1(name: String, age: Int)
异常处理
语法处理上和 Java 类似,但是又不尽相同
注意事项
(1)Java 语言按照 try—catch—finally 的方式来处理异常
(2)不管有没有异常捕获,都会执行 finally,因此通常可以在 finally 代码块中释放资源
(3)可以有多个 catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错
scala异常护理
Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)”异常,
即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。
异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。
因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异
常写在前,把具体的异常写在后,在 Scala 中也不会报错,但这样是非常不好的编程风格。
object Test01_Exception {
def main(args: Array[String]): Unit = {
try{
val n = 10 / 0
} catch {
case e: ArithmeticException => {
println("发生算术异常")
}
case e: Exception => {
println("发生一般异常")
}
} finally {
println("处理结束")
}
}
}
用 throw 关键字,抛出一个异常对象。所有异常都是 Throwable 的子类型。throw 表
达式是有类型的,就是 Nothing,因为 Nothing 是所有类型的子类型,所以 throw 表达式可
以用在需要类型的
def test():Nothing = {
throw new Exception("不对")
}
java 提供了 throws 关键字来声明异常。可以使用方法定义声明异常。它向调用者函
数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在 try-catch
块中,以避免程序异常终止。在 Scala 中,可以使用 throws 注解来声明异常
def main(args: Array[String]): Unit = {
f11()
}
@throws(classOf[NumberFormatException])
def f11()={
"abc".toInt
}
隐式转换
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次
隐式函数
说明
隐式转换可以在不需改任何代码的情况下,扩展某个类的功能
隐式参数
普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时,
就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数
隐式类
在 Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用
1)隐式类
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级
object Test02_Implicit {
def main(args: Array[String]): Unit = {
val new12 = new MyRichInt(12)
println(new12.myMax(15))
// 1. 隐式函数
implicit def convert(num: Int): MyRichInt = new MyRichInt(num)
println(12.myMax(15))
println("============================")
// 2. 隐式类
implicit class MyRichInt2(val self: Int) {
// 自定义比较大小的方法
def myMax2(n: Int): Int = if ( n < self ) self else n
def myMin2(n: Int): Int = if ( n < self ) n else self
}
println(12.myMin2(15))
println("============================")
// 3. 隐式参数
implicit val str: String = "alice"
// implicit val str2: String = "alice2"
implicit val num: Int = 18
def sayHello()(implicit name: String): Unit = {
println("hello, " + name)
}
def sayHi(implicit name: String = "atguigu"): Unit = {
println("hi, " + name)
}
sayHello
sayHi
// 简便写法
def hiAge(): Unit = {
println("hi, " + implicitly[Int])
}
hiAge()
}
}
// 自定义类
class MyRichInt(val self: Int) {
// 自定义比较大小的方法
def myMax(n: Int): Int = if ( n < self ) self else n
def myMin(n: Int): Int = if ( n < self ) n else self
}
泛型
协变和逆变
语法
class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变
说明:
协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。
逆变:Son 是 Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。
不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系
泛型上下限
1)语法
Class PersonList[T <: Person]{ //泛型上限}
Class PersonList[T >: Person]{ //泛型下限}2)说明
泛型的上下限的作用是对传入的泛型进行限定。
上下文限定
1)语法
def f[A : B](a: A) = println(a) //等同于 def f[A](a:A)(implicit arg:B[A])=println(
2)说明
上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A :
Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]]
获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。
object Test03_Generics {
def main(args: Array[String]): Unit = {
// 1. 协变和逆变
val child: Parent = new Child
// val childList: MyCollection[Parent] = new MyCollection[Child]
val childList: MyCollection[SubChild] = new MyCollection[Child]
// 2. 上下限
def test[A <: Child](a: A): Unit = {
println(a.getClass.getName)
}
test[SubChild](new SubChild)
}
}
// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
// 定义带泛型的集合类型
class MyCollection[-E] {}