Kotlin 的那些骚操作
<p>最近在学习 Kotlin 这门编程语言,不得不感叹 Kotlin 这语言是真的骚。</p>
<h1 id="重载操作符"><a href="#重载操作符" class="headerlink" title="重载操作符"></a>重载操作符</h1><h2 id="在伴生对象重载-invoke-操作符"><a href="#在伴生对象重载-invoke-操作符" class="headerlink" title="在伴生对象重载 invoke 操作符"></a>在伴生对象重载 invoke 操作符</h2><pre><code class="kotlin">interface BotUser {
val id: Long
val name: String
val description: String
companion object {
operator fun invoke(
id: Long,
name: String = "",
description: String = ""
): BotUser {
return BotUserImpl(id, name, description)
}
}
}
class BotUserImpl(...) { ... }
</code></pre>
<p>如果你这样做的话,可以让接口或者抽象类仿佛看起来能够被实例化一样(看似调用“构造方法”,实际上是调用了 invoke() )。</p>
<p><strong>用途:</strong>项目中途将某个类抽象为一个接口,并将原有类作为此接口的默认实现,这样可以做到源代码的兼容(对 Java 代码以及二进制仍不兼容,毕竟本质不同)。</p>
<blockquote>
<p>代码来自我的项目 “MoeCraftBotNG”</p>
</blockquote>
<h2 id="其他操作符重载"><a href="#其他操作符重载" class="headerlink" title="其他操作符重载"></a>其他操作符重载</h2><p>一般可以重载 <code>get()</code> <code>set()</code> 来使对象能够按数组、字典一样去操作。</p>
<p>为对象编写 <code>contains()</code> 方法时(判断某个元素是否“属于”此对象),可以顺便为此方法加个 <code>operator</code> 关键字可以让 <code>in</code> 关键字支持这个对象</p>
<p>然而操作符重载还是慎用为妙,众所周知这个特性就是被 C++ 玩坏的,当你 java 甚至以不支持操作符重载为荣。好在 Kotlin 在这方面也比较节制。</p>
<h1 id="协程"><a href="#协程" class="headerlink" title="协程"></a>协程</h1><p>如果想让你的 suspend 函数被 Java 用户友善地调用,防止你被人砍,你可以:</p>
<p>用 Kotlin 编写一个类:</p>
<pre><code class="kotlin">class Coroutines {
/**
* 获取在 Java 代码中调用 Kotlin Suspend 函数所需的最后一个参数 (Continuation)
* @param onFinished 当suspend函数执行完毕后所调用的回调。若 Throwable 不为 null 则说明执行失败。否则为执行成功
* @param dispatcher 协程执行线程的类型。可以为 Dispatchers.Default(CPU密集型) Dispatchers.Main(主线程) Dispatchers.IO(IO密集型)
*/
@JvmOverloads
fun <R> getContinuation(onFinished: BiConsumer<R?, Throwable?>, dispatcher: CoroutineDispatcher = Default): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
//注意 Result 是 inline class,不可直接给出去
onFinished.accept(result.getOrNull(), result.exceptionOrNull())
}
}
}
}
</code></pre>
<p>kotlin suspend 函数的调用难点在于最后一个参数,这个参数是 suspend 函数自动生成的,但是在 Java 方面处理起来却十分棘手。你可以提供这样的一个类来生成最后一个参数的值。</p>
<p>然后在 Java 中,就可以这样调用了:</p>
<pre><code class="java">Coroutines coroutines = new Coroutines();
//假设一个 suspend fun login(username: String, password: String): RequestResult,位于 object UserUtils
UserUtils.INSTANCE.login("user", "pass", coroutines.getContinuation(
(result, throwable) -> {
//suspend fun执行结束的回调
System.out.println("Coroutines finished");
System.out.println("Result: " + result);
System.out.println("Exception: " + throwable);
}
)
);
</code></pre>
<p>另外,也可以使用 <code>org.jetbrains.kotlinx:kotlinx-coroutines-jdk8</code>,这个库可以让 suspend fun 返回 <code>CompletableFuture<></code> 以便在 java 使用</p>
<pre><code class="kotlin">fun doSomethingAsync(): CompletableFuture<List<MyClass>> =
GlobalScope.future { doSomething() } //返回 CompletableFuture 包装的 suspend fun doSomething()
</code></pre>
<h1 id="内置函数"><a href="#内置函数" class="headerlink" title="内置函数"></a>内置函数</h1><p>Kotlin 有很多实用的内置函数,只提几个。</p>
<h2 id="run"><a href="#run" class="headerlink" title="run()"></a>run()</h2><p>run() 字面意思,用于执行一个任意代码块,并返回代码块的返回值</p>
<p>run() 有两种,签名如下:</p>
<pre><code class="kotlin">public inline fun <R> run(block: () -> R): R
public inline fun <T, R> T.run(block: T.() -> R): R
</code></pre>
<p>第一种 run() 在为构造函数委托传递参数时特别有用,例如有一个类和两个次构造函数</p>
<pre><code class="kotlin">class ManagedJavaProperties(val inputStream: InputStream, val outputStream: OutputStream? = null) {
constructor(file: File): this(file.inputStream(), file.outputStream())
constructor(fileName: String): this(???) //需要进行处理才能委托给其他构造函数
}
</code></pre>
<p>第二个次构造函数中需要对 fileName 进行一些处理,此时需要 run() 登场了:</p>
<pre><code class="kotlin">class ... {
constructor(fileName: String): this(
kotlin.run {
val file = File(fileName)
if (!file.exists()) {
file.createNewFile()
}
file
}
)
}
</code></pre>
<p>第二种 run() 则是将调用者当作 this 传递给 lambda,除此之外和第一种完全相同</p>
<p>run() 也可以用于防止代码块内变量污染当前作用域。(类似于 Java 的 <code>{ }</code> )</p>
<h1 id="抑制错误"><a href="#抑制错误" class="headerlink" title="抑制错误"></a>抑制错误</h1><p>Kotlin 的 <code>@Suppress</code> 注解不仅可以抑制警告,还可以抑制任何错误。具体的错误名字可以到 kotlin 编译器项目按错误提示寻找。</p>