Kotlin的一些技巧和迂回操作

Author Avatar
Dexlind 1月 08, 2018
  • 在其它设备中阅读本文章

RT,不定期更新。

递归的 Lambda 表达式

刚才在某个 Kotlin 裙里看到有人在问:

是不是lambda无法递归

举个例子,我们可以写一个简单的递归函数:

fun a() { println("1551"); a() }
a() // 打印出很多1551

如果要写成 Lambda 呢?这样的代码会报错:

val a: () -> Unit = { println("1551"); a() }

我们自然是不能直接写这样的代码的,它会说 a 没有定义。解决方法当然是使用 lateinit

lateinit var a: () -> Unit
a = { println("1551"); a() }
a() // 打印出很多1551

更进一步:匿名 Lambda 表达式的递归

正统的「Lambda演算」里面的函数全部都是匿名函数,需要使用「不动点组合子」实现递归:

// 这是javascript
(f =>
  (x => f(y => x(x)(y)))
  (x => f(y => x(x)(y)))
)(func => () => {
    console.log("1551"); func()
})() // 打印出很多1551

上面的那一坨 f => (x => f(y => x(x)(y)))(x => f(y => x(x)(y))) 即是「Z组合子」。(读者可以思考一下为什么这里我给了 JavaScript 的例子是而不是 Kotlin(逃

阻止编译器添加对非空类型的函数参数的 NullCheck

总所周知,当一个函数的参数是非空类型时,Kotlin编译器会在方法入口处加一行检查入参是否为空的代码。比如说 main 函数:

fun main(args: Array<String>) {}

经过编译后,再反编译成Java:

public static final void main(@NotNull String[] args) {
    Intrinsics.checkParameterIsNotNull(args, "args");
}

可恶!辣鸡编译器自作主张!我不想要这行代码!

如果不想编译器生成这些代码,把这几个编译器参数 -Xno-call-assertions-Xno-param-assertions-Xno-receiver-assertions 传给Kotlin编译器即可。

传递编译器参数的方法:

使用IDEA调用编译器的情况:

Project 设置:File -> Settings -> 找到 Kotlin Compiler -> Additional command line parameters

Module 设置:File -> Project Structure -> Module -> 找到你的Module里面的Kotlin设置 -> Additional command line parameters

使用Gradle Kotlin DSL的情况:

// build.gradle.kts
tasks.withType<KotlinCompile> {
    // 加上下面这行
    kotlinOptions.freeCompilerArgs = listOf("-Xno-call-assertions", "-Xno-param-assertions", "-Xno-receiver-assertions")
}

PS:注意IDEA的 Delegate IDE build/run actions to gradle 这个选项是否勾选的区别。

给data class自定义getter和setter

data class SomeClass(var name: String)

众所周知 Kotlin 不允许给声明在主构造器里面的属性写自定义getter、setter,主要是为了防止有好事者乱写,破坏规则就不好了。所以迂回操作如下:

data class SomeClass(private var _name: String) {
    var name: String
        get() = _name
        set(value) { _name = value }
}

解释:private_name不会生成getter和setter,你再把你想写的getter和setter添上就好。这样SomeClass里面就有3样东西:String _nameString getName()void setName(String)(以及data class根据_name自动生成的那些)。

缺点很明显,toString 生成的字符串会比较丑。

流的读取

普通青年:

// java 代码
void someFunc(InputStream in, OutputStream out) throws IOException {
    int read;
    while ((read = in.read()) != -1) {
        out.write(read);
    }
}

文艺青年:

fun someFunc(`in`: InputStream, out: OutputStream) {
    var read: Int = -1
    while ({ read = `in`.read();read }() != -1) {
        out.write(read)
    }
}

二逼青年:

fun someFunc(`in`: InputStream, out: OutputStream) {
    var read: Int = `in`.read()
    while (read != -1) {
        out.write(read)
        read = `in`.read()
    }
}

群里的优秀的青年(不是我):

fun someFunc(`in`: InputStream, out: OutputStream) {
    var read: Int = -1
    while (`in`.read().also { read = it } != -1) {
        out.write(read)
    }
}

限制扩展的作用域(防止污染命名空间)

注意:此技巧并不稳定,可能在未来被官方干掉。

// 把扩展丢进一个object里面
object StringExtension {
    @JvmStatic fun String.fuck() = println("fuck $this")
}
// 使用说明
fun test() {
    // 下面这行被注释掉的代码不能通过编译
    // "kotlin".fuck()

    // 你要这么用,将MyExtentions塞进上下文(即this)
    with(StringExtention) {
        "kotlin".fuck()
    }
}
// 或者手动引入
import StringExtension.fuck

fun test() {
    "kotlin".fuck()
}
// 以下是夏姬八写,别模仿
interface Extension

inline fun <T : Extension, R> T.use(block: T.() -> R) = this.block()

object StringExtension : Extension {
    @JvmStatic fun String.fuck() = println("fuck $this")
}

object IntExtension : Extension {
    @JvmStatic fun Int.love() = println("I love $this")
}

fun test() {
    StringExtension.use { "kotlin".fuck() }

    IntExtension.use { 1551.love() }
}

链式调用时输出中间值

inline fun <T> T.println(): T = printlnBy { it }

inline fun <T, U> T.printlnBy(selector: (T) -> U): T = this.also { println(selector(it)) }

fun test() {
    listOf(1, 2, 3).asSequence()
            .map { it * 3 }.printlnBy { it.sum() } // <==这里
            .filter { it and 1 == 0 }
            .sum().println() // <==还有这里
}
// 输出:
// 18
// 6

注意副作用,别夏姬八用!

如果是集合操作,可以考虑使用 onEach 这个高阶函数,例如onEach { println(it) }

知识共享许可协议
本作品采用知识共享 署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,转载请注明出处。

本文链接:https://aisia.moe/2018/01/07/kotlin-jiqiao/