Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

扩展属性

就像函数可以是扩展函数一样,属性也可以是扩展属性

扩展属性的接收器类型规范与扩展函数的语法类似——扩展的类型直接放在函数或属性名称之前:

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 找到。