第 9 章 异常

语法处理上和 Java 类似,但是又不尽相同。

9.1 Java 异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ExceptionDemo {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
int c = a / b;
}catch (ArithmeticException e){
// catch 时,需要将范围小的写到前面
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("finally");
}
}
}

注意事项

(1)Java 语言按照 try—catch—finally 的方式来处理异常

(2)不管有没有异常捕获,都会执行 finally,因此通常可以在 finally 代码块中释放资源。

(3)可以有多个 catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。

9.2 Scala 异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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("处理结束")
}
}
}

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 表达式可以用在需要类型的地方

1
2
3
def test():Nothing = {
throw new Exception("不对")
}

6)java 提供了 throws 关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在 try-catch块中,以避免程序异常终止。在 Scala 中,可以使用 throws 注解来声明异常

1
2
3
4
5
6
7
def main(args: Array[String]): Unit = {
f11()
}
@throws(classOf[NumberFormatException])
def f11()={
"abc".toInt
}

第 10 章 隐式转换

当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译

10.1 隐式函数

1)说明

隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。

2)案例实操

需求:通过隐式转化为 Int 类型增加方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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))

}
}

// 自定义类
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
}

10.2 隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。

1)说明

(1)同一个作用域中,相同类型的隐式值只能有一个

(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。

(3)隐式参数优先于默认参数

2)案例实操

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

object Test02_Implicit {
def main(args: Array[String]): Unit = {
// 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("zzxx") 报错:隐式参数通过柯里化来实现 第一个表明参数为空
sayHi("zzxx") //输出 hi,zzxx
sayHi("") //输出 hi,alice 隐式参数优先于默认参数

// 简便写法
def hiAge(): Unit = {
println("hi, " + implicitly[Int]) //寻找同一个作用域中,Int类型的隐式值
}
hiAge()
}
}

10.3 隐式类

在 Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。

1)隐式类说明

(1)其所带的构造参数有且只能有一个

(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的

2)案例实操

1
2
3
4
5
6
7
8
9
10
11
12
object Test02_Implicit {
def main(args: Array[String]): Unit = {

// 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))
}
}

10.4 隐式解析机制

1)说明

(1)首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况)

(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。

类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象

2)案例实操

11 章 泛型

11.1 协变和逆变

1)语法

1
2
3
4
5
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)案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
object Test03_Generics {
def main(args: Array[String]): Unit = {
// 1. 协变和逆变
val child: Parent = new Child
// val childList: MyCollection[Parent] = new MyCollection[Child] // 需要class MyCollection[+E] {}
val childList: MyCollection[SubChild] = new MyCollection[Child]

}
}

// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}

// 定义带泛型的集合类型
class MyCollection[-E] {}

11.2 泛型上下限

1)语法

1
2
3
4
Class PersonList[T <: Person]{ //泛型上限
}
Class PersonList[T >: Person]{ //泛型下限
}

2)说明

泛型的上下限的作用是对传入的泛型进行限定。

3)案例实操

1
2
3
4
5
6
// 2. 上下限
def test[A <: Child](a: A): Unit = {
println(a.getClass.getName)
}

test[SubChild](new SubChild)

11.3 上下文限定

1)语法

1
2
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]]

获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

1
2
3
implicit val x = 1
val y = implicitly[Int]
val z = implicitly[Double]

3)案例实操

1
2
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)