除了同步阻塞代码块,我们可以用一个表达式来实现同步阻塞。 “Any” 是 Kotlin 的对象版本。所有的类型都是 “Any” 的子类型。我们可以对它增加一个方法,该方法输入是一个函数,然后执行这个函数的代码,这段代码会同步该实例。如果我们需要同样的锁,或者一些对象需要被锁住,我们可以把这段代码放在函数内部的锁起来。然后我们把它传给每个实例的这个方法中。这个方法简化了调用者的代码,而且清晰很多。
简单清晰的锁
另一个十分酷的应用存在于我们常用的锁的情景中,我们常常忘记在一些操作之前完成锁的操作。我们可以写这样的类,这个类只允许你访问你需要用锁才能操作的资源。
[代码]csharp代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
data class Lock<t>(private val obj: T) {
public fun acquire(func: (T) -> Unit) {
synchronized (obj) {
func(obj)
}
}
}
val readerLock = Lock(JsonReader(stream))
// Later
readerLock.acquire {
println(it.readString())
}</t>
在这个例子里面,我们创建了依赖流的 JsonReader。设想不论什么原因我们需要多线程同时访问它,这样我们就必须使用锁,锁会帮助我们管理同步的问题。
然后在后面的代码中,我们调用了这个 acquire 的方法,它会在这个 JsonReader 的实例上实现同步,然后把它传给我们提供的函数。所以在这种情况下,我们会在这个 JsonReader 里面再次析构,于是我们需要使用锁。但现在我们的代码根本没有处理锁的问题。我们也没有显式的同步,也没有为 JsonReader 创建锁。可是访问 JsonReader 不使用锁是不可能的。
避免 Kotlin 带来的泄露
前面,我提到过关于 data 的代码,定义了一个这样的值:
[代码]csharp代码:
1
val notEmpty: (String) -> Boolean { !it.isEmpty() }
Kotlin 实现函数表达式的方法和在 Java 里面使用类的方法是一样的。好处是 Kotlin 并没有创建对于外部范围的引用,因为没有这样的类。这样避免了可能的上下文泄漏。
我们需要关心的就是传入的数据。它会导致创建一个静态的单例实例而且没有引用。用这些函数表达式根本不可能产生上下文泄漏。但是在最上层,我们两个是一模一样的。
扩展函数表达式的例子
扩展函数 – 给一个类型加入函数但是不修改原来的类型。
函数表达式 – 未定义的函数体被用作表达式(i.e.,date)
Higher-Order 函数 – 一个参数是函数或者返回是函数的函数。
扩展函数表达式是上述三个概念的综合体,这是个强大的而且可以创建清晰的 API 的方法。为了说明这点,我将使用一个 databases 的 API 做为例子,把它改造成一个更加整洁的、没有无效代码的 API。
[代码]csharp代码:
1
2
3
4
5
6
7
db.beginTransaction();
try {
db.delete("users", "first_name = ?", new String[] { "Jake" });
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
如果你想在一个 transaction 里面执行一个 statement 的话,这是你必须写的六行代码。我们开始一个 transaction,我们把它放在 “try finally” 里面,然后我们标记这个 transaction 为成功。如果它抛出或者不在 “finally” 里面抛出异常,我们需要结束 transaction.
这是一个容易带来 bug 的代码,容易健忘的代码和应该重构的代码。任何事情都有可能出现错误,在这里你不小心交换了两个事情,然后突然,你就会有一个很难找到原因的 bug。 或者它会在运行时 crash。
扩展函数表达式允许我们解决这个问题。我们现在能给 database 自己增加一个方法, 这将给现有的代码构建一个防护墙。
[代码]csharp代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
fun SQLiteDatabase.inTransaction(func: (SQLiteDatabase) -> Unit) {
beginTransaction()
try {
func(this)
setTransactionSuccessful()
} finally {
endTransaction()
}
}
db.inTransaction {
it.db.delete("users", "first_name = ?", arrayOf("Jake"))
}