每天一个秃头小技巧

【kotlin 】内联类(value class / inlin

2022-01-02  本文已影响0人  littlefogcat

官方文档:https://kotlinlang.org/docs/inline-classes.html

注:inline class 关键字已经被废弃,取而代之的是value class。现在使用内联类需要定义类为value class,并使用@JvmInline注解进行标注。

一、使用场景

有时候,根据业务需求,我们需要一些包装类。但是,包装类在运行时会造成一些不可避免的额外开支,比如堆上分配的额外空间。尤其是对于基本类型的包装类——因为基本类型在运行时会有很多其他优化,而包装类型没有。于是,内联类便应运而生了。

内联类在编码时作为一个其他类型的包装类使用,而在运行时会被拆开作为其内部值类型使用。

例如,当我们设计了一个动画:

class Animation(duration: Int) {
    // ...
}

duration 参数可能会让人迷惑:它的单位是什么?秒或者毫秒?(虽然注释可以解决一切问题,但它不在讨论范围之内)这个时候,就可以用到内联类。

我们可以创建一系列的内联类,来表示不同的时间单位:

@JvmInline
value class Millis(val value: Int) 
@JvmInline
value class Second(val value: Int)
// ...

假使 duration 参数单位是毫秒,那么将其类型修改为 Millis 类型即可:

class Animation(duration: Millis) {
    // ...
}

这样,当创建 Animation 对象的时候,就需要强制传入一个 Millis 类型的对象;如果传入的是一个 Second 类型的对象,编译器就会报错。

二、内联类允许的成员

内联类允许函数、init 块、以及没有 backing field 的属性。

@JvmInline
value class Name(val s: String) {
    init {
        require(s.length > 0) { }
    }

    val length: Int
        get() = s.length

    fun greet() {
        println("Hello, $s")
    }
}

fun main() {
    val name = Name("Kotlin")
    name.greet() // method `greet` is called as a static method
    println(name.length) // property getter is called as a static method
}

三、其他

1. 继承

内联类只允许继承接口,而不允许继承类,也不允许被其他类继承。

2. 与 typealias 的相比

在读取值的时候,value classtypealias 起到了类似的作用;但是,当进行赋值的时候,情况就变得不一样了。typealias 允许

假设我们现在使用一个类型 Name,表示一个字符串值。同时,有两个函数 setStringsetName

fun setName(name: Name) {}
fun setString(string: String) {}

在使用 typealias 的情况下,不管是 setString 还是 setName,均可以传入 Name 或是 String 类型的参数:

typealias Name = String
fun main() {
    val name: Name = "Bob"
    setName("Bob") // √
    setString(name) // √
}

但是,如果使用的是内联类,则二者均是不被允许的:

@JvmInline
value class Name(value: String)
fun main() {
    val name: Name = "Bob"
    setName("Bob") // ×
    setString(name) // ×
}
上一篇 下一篇

猜你喜欢

热点阅读