throw and Exception Handling
Inspect one language lane at a time so line-level text and code deltas stay readable.
Diff Lane
English
0 modified sections0 code block delta0 anchor delta
Diff Lane
中文
3 modified sections0 code block delta0 anchor delta
modifiedthrow 和处理异常text+7 lines, -2 lines
v1.0.5
Section Text
1
上文介绍了如何自定义异常,接下来学习如何抛出和处理异常。2
3
- 由于异常是 `class` 类型,只需要按 class 对象的构建方式去创建异常即可。如表达式 `FatherException()` 即创建了一个类型为 `FatherException` 的异常。4
- 仓颉语言提供 `throw` 关键字,用于抛出异常。用 `throw` 来抛出异常时,`throw` 之后的表达式必须是 `Exception` 的子类型(同为异常的 `Error` 不可以手动 `throw` ),如 `throw ArithmeticException("I am an Exception!")` (被执行到时)会抛出一个算术运算异常。5
- `throw` 关键字抛出的异常需要被捕获处理。若异常没有被捕获,则由系统调用默认的异常处理函数。6
7
异常处理由 `try` 表达式完成,可分为:8
9
- 不涉及资源自动管理的普通 try 表达式。10
- 会进行资源自动管理 try-with-resources 表达式。v1.1.0
Section Text
1
上文介绍了如何自定义异常,接下来学习如何抛出和处理异常。2
3
- 由于异常是 `class` 类型,只需要按 class 对象的构建方式去创建异常即可。如表达式 `FatherException()` 即创建了一个类型为 `FatherException` 的异常。<!--Del-->4
- 仓颉支持创建异常链(仅 `Exception`,不包含 `Error`),如表达式 `let fatherException = FatherException("this is message", causeException)`,`causeException` 也是一个异常,是 `fatherException` 的触发原因。在打印异常的调用栈时,会递归打印触发原因。<!--DelEnd-->5
- 仓颉语言提供 `throw` 关键字,用于抛出异常。用 `throw` 来抛出异常时,`throw` 之后的表达式必须是 `Exception` 的子类型(同为异常的 `Error` 不可以手动 `throw` ),如 `throw ArithmeticException("I am an Exception!")` (被执行到时)会抛出一个算术运算异常。6
- `throw` 关键字抛出的异常需要被捕获处理。若异常没有被捕获,则由系统调用默认的异常处理函数。7
8
> **注意:**9
>10
> 开发者可以调用如下 `Thread` 类的静态函数,对未捕获的 `Exception` 注册自定义的异常处理函数:11
>12
> - `public static func handleUncaughtExceptionBy(exHandler: (Thread, Exception) -> Unit): Unit`13
14
异常处理由 `try` 表达式完成,可分为:15
16
- 不涉及资源自动管理的普通 try 表达式。17
- 会进行资源自动管理的 try-with-resources 表达式。modified普通 try 表达式code
v1.0.5
Section Text
1
普通 try 表达式包括三个部分:try 块,catch 块和 finally 块。2
3
- try 块,以关键字 `try` 开始,后面紧跟一个由表达式与声明组成的块(用一对花括号括起来,定义了新的局部作用域,可以包含任意表达式和声明,后简称“块”),try 后面的块内可以抛出异常,并被紧随的 catch 块所捕获并处理(如果不存在 catch 块或未被捕获,则在执行完 finally 块后,该异常继续被抛出)。4
5
- catch 块,一个普通 try 表达式可以包含零个或多个 catch 块(当没有 catch 块时必须有 finally 块)。每个 catch 块以关键字 `catch` 开头,后跟一条 `catchPattern` 和一个块,`catchPattern` 通过模式匹配的方式匹配待捕获的异常。一旦匹配成功,则交由其后跟随的块进行处理,并且忽略它后面的其他 catch 块。当某个 catch 块可捕获的异常类型均可被定义在它前面的某个 catch 块所捕获时,会在此 catch 块处报“catch 块不可达”的 warning。6
7
- finally 块,以关键字 `finally` 开始,后面紧跟一个块。原则上,finally 块中主要实现一些“善后”的工作,如释放资源等,且要尽量避免在 finally 块中再抛异常。并且无论异常是否发生(即无论 try 块中是否抛出异常),finally 块内的内容都会被执行(若异常未被处理,执行完 finally 块后,继续向外抛出异常)。一个 try 表达式在包含 catch 块时可以不包含 finally 块,否则必须包含 finally 块。8
9
`try` 后面紧跟的块以及每个 `catch` 块的作用域互相独立。10
11
下面是一个只有 try 块和 catch 块的简单示例:12
13
<!-- verify -->14
15
16
执行结果为:17
18
19
`catchPattern` 中引入的变量作用域级别与 `catch` 后面的块中变量作用域级别相同,在 catch 块中再次引入相同名字会触发重定义错误。例如:20
21
<!-- compile.error -->22
23
24
下面是带有 finally 块的 try 表达式的简单示例:25
26
<!-- verify -->27
28
29
执行结果为:30
31
32
try 表达式可以出现在任何允许使用表达式的地方。try 表达式的类型的确定方式,与 `if`、`match` 表达式等多分支语法结构的类型的确定方式相似,为 finally 分支除外的所有分支的类型的最小公共父类型。例如下面代码中的 try 表达式和变量 `x` 的类型均为 E 和 D 的最小公共父类型 D;finally 分支中的 `C()` 并不参与公共父类型的计算(若参与,则最小公共父类型会变为 `C`)。33
34
另外,当 `try` 表达式的值没有被使用时,其类型为 `Unit`,不要求各分支的类型有最小公共父类型。35
36
<!-- compile -->Code 1 · cangjie
1
main() {2
try {3
throw NegativeArraySizeException("I am an Exception!")4
} catch (e: NegativeArraySizeException) {5
println(e)6
println("NegativeArraySizeException is caught!")7
}8
println("This will also be printed!")9
}Code 2 · text
1
NegativeArraySizeException: I am an Exception!2
NegativeArraySizeException is caught!3
This will also be printed!Code 3 · cangjie
1
main() {2
try {3
throw NegativeArraySizeException("I am an Exception!")4
} catch (e: NegativeArraySizeException) {5
println(e)6
let e = 0 // Error, redefinition7
println(e)8
println("NegativeArraySizeException is caught!")9
}10
println("This will also be printed!")11
}Code 4 · cangjie
1
main() {2
try {3
throw NegativeArraySizeException("NegativeArraySizeException")4
} catch (e: NegativeArraySizeException) {5
println("Exception info: ${e}.")6
} finally {7
println("The finally block is executed.")8
}9
}Code 5 · text
1
Exception info: NegativeArraySizeException: NegativeArraySizeException.2
The finally block is executed.Code 6 · cangjie
1
open class C { }2
open class D <: C { }3
class E <: D { }4
main () {5
let x = try {6
E()7
} catch (e: Exception) {8
D()9
} finally {10
C()11
}12
013
}v1.1.0
Section Text
1
普通 try 表达式包括三个部分:try 块,catch 块和 finally 块。2
3
- try 块,以关键字 `try` 开始,后面紧跟一个由表达式与声明组成的块(用一对花括号括起来,定义了新的局部作用域,可以包含任意表达式和声明,后简称“块”),try 后面的块内可以抛出异常,并被紧随的 catch 块所捕获并处理(如果不存在 catch 块或未被捕获,则在执行完 finally 块后,该异常继续被抛出)。4
5
- catch 块,一个普通 try 表达式可以包含零个或多个 catch 块(当没有 catch 块时必须有 finally 块)。每个 catch 块以关键字 `catch` 开头,后跟一条 `catchPattern` 和一个块,`catchPattern` 通过模式匹配的方式匹配待捕获的异常。一旦匹配成功,则交由其后跟随的块进行处理,并且忽略它后面的其他 catch 块。当某个 catch 块可捕获的异常类型均可被定义在它前面的某个 catch 块所捕获时,会在此 catch 块处报“catch 块不可达”的 warning。6
7
- finally 块,以关键字 `finally` 开始,后面紧跟一个块。原则上,finally 块中主要实现一些“善后”的工作,如释放资源等,且要尽量避免在 finally 块中再抛异常。并且无论异常是否发生(即无论 try 块中是否抛出异常),finally 块内的内容都会被执行(若异常未被处理,执行完 finally 块后,继续向外抛出异常)。一个 try 表达式在包含 catch 块时可以不包含 finally 块,否则必须包含 finally 块。8
9
`try` 后面紧跟的块以及每个 `catch` 块的作用域互相独立。10
11
下面是一个只有 try 块和 catch 块的简单示例:12
13
<!-- verify -->14
15
16
执行结果为:17
18
19
`catchPattern` 中引入的变量作用域级别与 `catch` 后面的块中变量作用域级别相同,在 catch 块中再次引入相同名字会触发重定义错误。例如:20
21
<!-- compile.error -->22
23
24
下面是带有 finally 块的 try 表达式的简单示例:25
26
<!-- verify -->27
28
29
执行结果为:30
31
32
try 表达式可以出现在任何允许使用表达式的地方。try 表达式的类型的确定方式,与 `if`、`match` 表达式等多分支语法结构的类型的确定方式相似,为 finally 分支除外的所有分支的类型的最小公共父类型。例如下面代码中的 try 表达式和变量 `x` 的类型均为 E 和 D 的最小公共父类型 D;finally 分支中的 `C()` 并不参与公共父类型的计算(若参与,则最小公共父类型会变为 `C`)。33
34
另外,当 `try` 表达式的值没有被使用时,其类型为 `Unit`,不要求各分支的类型有最小公共父类型。35
36
<!-- compile -->Code 1 · cangjie
1
main() {2
try {3
throw NegativeArraySizeException("I am an Exception!")4
} catch (e: NegativeArraySizeException) {5
println(e)6
println("NegativeArraySizeException is caught!")7
}8
println("This will also be printed!")9
}Code 2 · text
1
NegativeArraySizeException: I am an Exception!2
NegativeArraySizeException is caught!3
This will also be printed!Code 3 · cangjie
1
main() {2
try {3
throw NegativeArraySizeException("I am an Exception!")4
} catch (e: NegativeArraySizeException) {5
println(e)6
let e = 0 // Error, redefinition7
println(e)8
println("NegativeArraySizeException is caught!")9
}10
println("This will also be printed!")11
}Code 4 · cangjie
1
main() {2
try {3
throw NegativeArraySizeException("NegativeArraySizeException")4
} catch (e: NegativeArraySizeException) {5
println("Exception info: ${e}.")6
} finally {7
println("The finally block is executed.")8
}9
}Code 5 · text
1
Exception info: NegativeArraySizeException: NegativeArraySizeException.2
The finally block is executed.Code 6 · cangjie
1
open class C {}2
3
open class D <: C {}4
5
class E <: D {}6
7
main() {8
let x = try {9
E()10
} catch (e: Exception) {11
D()12
} finally {13
C()14
}15
016
}modifiedtry-with-resources 表达式code
v1.0.5
Section Text
1
try-with-resources 表达式主要是为了自动释放非内存资源。不同于普通 try 表达式,try-with-resources 表达式中的 catch 块和 finally 块均是可选的,并且 try 关键字其后的块之间可以插入一个或者多个 `ResourceSpecification` 用来申请一系列的资源(`ResourceSpecification` 并不会影响整个 try 表达式的类型)。这里所讲的资源对应到语言层面即指对象,因此 `ResourceSpecification` 其实就是实例化一系列的对象(多个实例化之间使用“,”分隔)。使用 try-with-resources 表达式的例子如下所示:2
3
<!-- compile -->4
5
6
程序输出结果为:7
8
9
`try` 关键字和 `{}` 之间引入的名字,其作用域与 `{}` 中引入的变量作用域级别相同,在 `{}` 中再次引入相同名字会触发重定义错误。10
11
<!-- compile.error -->12
13
14
try-with-resources 表达式中的 `ResourceSpecification` 的类型必须实现 Resource 接口:15
16
<!-- run -->17
18
19
需要说明的是,try-with-resources 表达式中一般没有必要再包含 catch 块和 finally 块,也不建议开发者再手动释放资源(逻辑冗余)。但是,如果需要显式地捕获 try 块或资源申请和释放过程中可能抛出的异常并处理,仍可在 try-with-resources 表达式中包含 catch 块和 finally 块:20
21
<!-- verify -->22
23
24
程序输出结果如下:25
26
27
try-with-resources 表达式的类型是 `Unit`。Code 1 · cangjie
1
class Worker <: Resource {2
var hasTools: Bool = false3
let name: String4
5
public init(name: String) {6
this.name = name7
}8
public func getTools() {9
println("${name} picks up tools from the warehouse.")10
hasTools = true11
}12
13
public func work() {14
if (hasTools) {15
println("${name} does some work with tools.")16
} else {17
println("${name} doesn't have tools, does nothing.")18
}19
}20
21
public func isClosed(): Bool {22
if (hasTools) {23
println("${name} hasn't returned the tool.")24
false25
} else {26
println("${name} has no tools")27
true28
}29
}30
public func close(): Unit {31
println("${name} returns the tools to the warehouse.")32
hasTools = false33
}34
}35
36
main() {37
try (r = Worker("Tom")) {38
r.getTools()39
r.work()40
}41
try (r = Worker("Bob")) {42
r.work()43
}44
try (r = Worker("Jack")) {45
r.getTools()46
throw Exception("Jack left, because of an emergency.")47
}48
}Code 2 · text
1
Tom picks up tools from the warehouse.2
Tom does some work with tools.3
Tom hasn't returned the tool.4
Tom returns the tools to the warehouse.5
Bob doesn't have tools, does nothing.6
Bob has no tools7
Jack picks up tools from the warehouse.8
Jack hasn't returned the tool.9
Jack returns the tools to the warehouse.10
An exception has occurred:11
Exception: Jack left, because of an emergency.12
at test.main()(xxx/xx.cj:xx)Code 3 · cangjie
1
class R <: Resource {2
public func isClosed(): Bool {3
true4
}5
public func close(): Unit {6
print("R is closed")7
}8
}9
10
main() {11
try (r = R()) {12
println("Get the resource")13
let r = 0 // Error, redefinition14
println(r)15
}16
}Code 4 · cangjie
1
interface Resource {2
func isClosed(): Bool // 离开 try-with-resources 作用域时,判断是否需要调用 close 函数释放资源3
func close(): Unit // 在 isClosed 返回 false 的场景下释放资源。4
}Code 5 · cangjie
1
class R <: Resource {2
public func isClosed(): Bool {3
true4
}5
public func close(): Unit {6
print("R is closed")7
}8
}9
10
main() {11
try (r = R()) {12
println("Get the resource")13
} catch (e: Exception) {14
println("Exception happened when executing the try-with-resources expression")15
} finally {16
println("End of the try-with-resources expression")17
}18
}Code 6 · text
1
Get the resource2
End of the try-with-resources expressionv1.1.0
Section Text
1
try-with-resources 表达式主要是为了自动释放非内存资源。不同于普通 try 表达式,try-with-resources 表达式中的 catch 块和 finally 块均是可选的,并且 try 关键字其后的块之间可以插入一个或者多个 `ResourceSpecification` 用来申请一系列的资源(`ResourceSpecification` 并不会影响整个 try 表达式的类型)。这里所讲的资源对应到语言层面即指对象,因此 `ResourceSpecification` 其实就是实例化一系列的对象(多个实例化之间使用“,”分隔)。使用 try-with-resources 表达式的例子如下所示:2
3
<!-- compile -->4
5
6
程序输出结果为:7
8
9
`try` 关键字和 `{}` 之间引入的名字,其作用域与 `{}` 中引入的变量作用域级别相同,在 `{}` 中再次引入相同名字会触发重定义错误。10
11
<!-- compile.error -->12
13
14
try-with-resources 表达式中的 `ResourceSpecification` 的类型必须实现 Resource 接口:15
16
<!-- run -->17
18
19
需要说明的是,try-with-resources 表达式中一般没有必要再包含 catch 块和 finally 块,也不建议开发者再手动释放资源(逻辑冗余)。但是,如果需要显式地捕获 try 块或资源申请和释放过程中可能抛出的异常并处理,仍可在 try-with-resources 表达式中包含 catch 块和 finally 块:20
21
<!-- verify -->22
23
24
程序输出结果如下:25
26
27
try-with-resources 表达式的类型是 `Unit`。Code 1 · cangjie
1
class Worker <: Resource {2
var hasTools: Bool = false3
let name: String4
5
public init(name: String) {6
this.name = name7
}8
public func getTools() {9
println("${name} picks up tools from the warehouse.")10
hasTools = true11
}12
13
public func work() {14
if (hasTools) {15
println("${name} does some work with tools.")16
} else {17
println("${name} doesn't have tools, does nothing.")18
}19
}20
21
public func isClosed(): Bool {22
if (hasTools) {23
println("${name} hasn't returned the tool.")24
false25
} else {26
println("${name} has no tools")27
true28
}29
}30
public func close(): Unit {31
println("${name} returns the tools to the warehouse.")32
hasTools = false33
}34
}35
36
main() {37
try (r = Worker("Tom")) {38
r.getTools()39
r.work()40
}41
try (r = Worker("Bob")) {42
r.work()43
}44
try (r = Worker("Jack")) {45
r.getTools()46
throw Exception("Jack left, because of an emergency.")47
}48
}Code 2 · text
1
Tom picks up tools from the warehouse.2
Tom does some work with tools.3
Tom hasn't returned the tool.4
Tom returns the tools to the warehouse.5
Bob doesn't have tools, does nothing.6
Bob has no tools7
Jack picks up tools from the warehouse.8
Jack hasn't returned the tool.9
Jack returns the tools to the warehouse.10
An exception has occurred:11
Exception: Jack left, because of an emergency.12
at test.main()(xxx/xx.cj:xx)Code 3 · cangjie
1
class R <: Resource {2
public func isClosed(): Bool {3
true4
}5
public func close(): Unit {6
print("R is closed")7
}8
}9
10
main() {11
try (r = R()) {12
println("Get the resource")13
let r = 0 // Error, redefinition14
println(r)15
}16
}Code 4 · cangjie
1
interface Resource {2
func isClosed(): Bool // 离开 try-with-resources 作用域时,判断是否需要调用 close 函数释放资源3
func close(): Unit // 在 isClosed 返回 false 的场景下释放资源。4
}Code 5 · cangjie
1
class R <: Resource {2
public func isClosed(): Bool {3
true4
}5
public func close(): Unit {6
print("R is closed")7
}8
}9
10
main() {11
try (r = R()) {12
println("Get the resource")13
} catch (e: Exception) {14
println("Exception happened when executing the try-with-resources expression")15
} finally {16
println("End of the try-with-resources expression")17
}18
}Code 6 · text
1
Get the resource2
End of the try-with-resources expression