扩展属性
就像函数可以是扩展函数一样,属性也可以是扩展属性。
扩展属性的接收器类型规范与扩展函数的语法类似——扩展的类型直接放在函数或属性名称之前:
fun ReceiverType.extensionFunction() { ... }
val ReceiverType.extensionProperty: PropType
get() { ... }
扩展属性需要一个自定义的 getter。每次访问时计算属性值:
// ExtensionProperties/StringIndices.kt
package extensionproperties
import atomictest.eq
val String.indices: IntRange
get() = 0 until length
fun main() {
"abc".indices eq 0..2
}
虽然你可以将没有参数的任何扩展函数转换为属性,但我们建议先考虑一下。在选择属性和函数之间的原因,Property Accessors中描述的原因也适用于扩展属性。只有当属性足够简单且提高了可读性时,才会优先选择属性而不是函数。
您可以定义一个通用的扩展属性。在这里,我们将 Introduction to Generics 中的 firstOrNull()
转换为扩展属性:
// ExtensionProperties/GenericListExt.kt
package extensionproperties
import atomictest.eq
val <T> List<T>.firstOrNull: T?
get() = if (isEmpty()) null else this[0]
fun main() {
listOf(1, 2, 3).firstOrNull eq 1
listOf<String>().firstOrNull eq null
}
Kotlin 样式指南 建议在函数抛出异常时使用函数而不是属性。
当没有使用泛型参数类型时,您可以将其替换为 *
。这称为星投影:
// ExtensionProperties/ListOfStar.kt
package extensionproperties
import atomictest.eq
val List<*>.indices: IntRange
get() = 0 until size
fun main() {
listOf(1).indices eq 0..0
listOf('a', 'b', 'c', 'd').indices eq 0..3
emptyList<Int>().indices eq IntRange.EMPTY
}
当使用 List<*>
时,您失去了关于 List
中包含的类型的所有具体信息。例如,List<*>
中的元素只能分配给 Any?
:
// ExtensionProperties/AnyFromListOfStar.kt
import atomictest.eq
fun main() {
val list: List<*> = listOf(1, 2)
val any: Any? = list[0]
any eq 1
}
我们不知道存储在 List<*>
中的值是否可为空,这就是为什么它只能分配给可为空的 Any?
类型。
练习和答案可以在 www.AtomicKotlin.com 找到。