- Introduction
- Part 1 Good Code
- Chapter 1 Safety
- 引言
- 第1条:限制可变性
- 第2条:最小化变量作用域
- 第3条:尽快消除平台类型
- 第4条:不要把推断类型暴露给外部
- Item 5 Specify Your Expectations On Arguments And State
- 第6条:尽可能使用标准库中提供的异常
- 第7条:当不能返回预期结果时,优先使用null o或Failure 作为返回值
- Item 8 Handle Nulls Properly
- 第9条:使用use关闭资源
- Item 10 Write Unit Tests
- Chapter 2 Readability
- Introduction
- Item 11 Design For Readability
- Item 12 Operator Meaning Should Be Consistent With Its Function Name
- Item 13 Avoid Returning Or Operating On Unit
- Item 14 Specify The Variable Type When It Is Not Clear
- Item 15 Consider Referencing Receivers Explicitly
- Item 16 Properties Should Represent State Not Behavior
- Item 17 Consider Naming Arguments
- Item 18 Respect Coding Conventions
- Part 2 Code Design
- Chapter 3 Reusability
- Introduction
- Item 19 Do Not Repeat Knowledge
- Item 20 Do Not Repeat Common Algorithms
- Item 21 Use Property Delegation To Extract Common Property Patterns
- Item 22 Use Generics When Implementing Common Algorithms
- Item 23 Avoid Shadowing Type Parameters
- Item 24 Consider Variance For Generic Types
- Item 25 Reuse Between Different Platforms By Extracting Common Modules
- Chapter 4 Abstraction Design
- Introduction
- Item 26 Each Function Should Be Written In Terms Of A Single Level Of Abstraction
- Item 27 Use Abstraction To Protect Code Against Changes
- Item 28 Specify API Stability
- Item 29 Consider Wrapping External API
- Item 30 Minimize Elements Visibility
- Item 31 Define Contract With Documentation
- Item 32 Respect Abstraction Contracts
- Chapter 5 Object Creation
- Introduction
- Item 33 Consider Factory Functions Instead Of Constructors
- Item 34 Consider A Primary Constructor With Named Optional Arguments
- Item 35 Consider Defining A DSL For Complex Object Creation
- Chapter 6 Class Design
- Introduction
- Item 36 Prefer Composition Over Inheritance
- Item 37 Use The Data Modifier To Represent A Bundle Of Data
- Item 38 Use Function Types Instead Of Interfaces To Pass Operations And Actions
- Item 39 Prefer Class Hierarchies To Tagged Classes
- Item 40 Respect The Contract Of Equals
- Item 41 Respect The Contract Of Hash Code
- Item 42 Respect The Contract Of Compare To
- Item 43 Consider Extracting Non Essential Parts Of Your API Into Extensions
- Item 44 Avoid Member Extensions
- Part 3 Efficiency
- Chapter 7 Make It Cheap
- Introduction
- Item 45 Avoid Unnecessary Object Creation
- Item 46 Use Inline Modifier For Functions With Parameters Of Functional Types
- Item 47 Consider Using Inline Classes
- Item 48 Eliminate Obsolete Object References
- Chapter 8 Efficient Collection Processing
- Introduction
- Item 49 Prefer Sequence For Big Collections With More Than One Processing Step
- Item 50 Limit The Number Of Operations
- Item 51 Consider Arrays With Primitives For Performance Critical Processing
- Item 52 Consider Using Mutable Collections
- Published with GitBook
第9条:使用use关闭资源
第9条:使用 use
关闭资源
有些资源不能自动关闭,当我们不再使用它们时,我们需要调用close
方法来手动关闭。我们在Kotlin/JVM中使用的Java标准库包含了很多这样的资源,例如
InputStream
和OutputStream
java.sql.Connection
,java.io.Reader
(FileReader
,BufferedReader
,CSSParser
)java.new.Socket
和java.util.Scanner
所有这些资源都实现了Closeable
接口,它继承自AutoCloseable
。
对于上面列举的这些例子,当我们确认不再需要该资源的时候,我们需要调用close
方法,因为这些资源的调用开销比较大并且它们被自动关闭的成本也较高(如果没有任何对该资源的引用,垃圾收集器最终会将它关闭,但这一过程所需的时间会比较久)。 因此,为了确保我们不会漏掉关闭它们,我们通常将这些资源调用放在在一个try-finally
块中,并在finally
中调用close
方法:
fun countCharactersInFile(path: String): Int {
val reader = BufferedReader(FileReader(path))
try {
return reader.lineSequence().sumBy { it.length }
} finally {
reader.close()
}
}
这样的结构是复杂且不正确的。它的不正确体现在close
可能会抛出异常且这个异常不会被捕获。此外,如果我们同时从try
和finally
块中抛出异常,那么只有一个异常会被正确地传递。 我们所期望的表现应该是后抛出的异常信息应该被添加到之前已经抛出的异常信息中。正确的实现很长并且很复杂,但这种处理很常见,因此Kotlin标准库中提供了use
函数。应该使用它来正确关闭资源和处理异常,此函数可用于任何Closeable
对象:
fun countCharactersInFile(path: String): Int {
val reader = BufferedReader(FileReader(path))
reader.use {
return reader.lineSequence().sumBy { it.length }
}
}
调用use
的对象(本例中为reader
)也会作为参数传递给lambda表达式,因此语法可以缩短:
fun countCharactersInFile(path: String): Int {
BufferedReader(FileReader(path)).use { reader ->
return reader.lineSequence().sumBy { it.length }
}
}
因为use
函数经常被用来操作文件,同时逐行读取文件也是一种很常见的操作,所以Kotlin标准库中提供了一个useLines
函数,它会返回给我们一个包含了文件中每一行内容(String
类型)的序列,并且在读取完毕之后会自动关闭文件资源:
fun countCharactersInFile(path: String): Int {
File(path).useLines { lines ->
return lines.sumBy { it.length }
}
}
这是一种适合用来处理大文件的方法,因为序列会按需读取每一行,因此每次调用对于内存的占用不会超过一行的内容所对应的内存大小。 但代价是这个序列只能使用一次,如果你需要多次遍历文件,则需要多次调用它。 useLines
函数同样也能作为一个表达式来调用。
fun countCharactersInFile(path: String): Int =
File(path).useLines { lines ->
lines.sumBy { it.length }
}
以上所有使用序列对文件进行操作的例子,都是比较合理的处理方法。因为这样我们可以每次只加载一行的内容,避免直接加载整个文件。更多相关内容请参考 Item 49: Prefer Sequence for big collections with more than one processing step.
Summary
使用use
对实现了Closeable
或AutoCloseable
的对象进行操作,是一个安全且简单的选择。当你需要操作一个文件时,考虑使用useLines
,它会生成一个序列来帮助你遍历每一行。