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

成员引用

您可以将成员引用作为函数参数传递。

对于函数、属性和构造函数,成员引用 可以替换只是调用相应函数、属性或构造函数的平凡 Lambda。

成员引用使用双冒号将类名与函数或属性分隔开。在这里,Message::isRead 是一个成员引用:

// MemberReferences/PropertyReference.kt
package memberreferences1
import atomictest.eq

data class Message(
  val sender: String,
  val text: String,
  val isRead: Boolean
)

fun main() {
  val messages = listOf(
    Message("Kitty", "Hey!", true),
    Message("Kitty", "Where are you?", false))
  val unread =
    messages.filterNot(Message::isRead)
  unread.size eq 1
  unread.single().text eq "Where are you?"
}

要筛选未读消息,我们使用库函数 filterNot(),该函数接受谓词。在我们的情况下,谓词指示消息是否已读。我们可以传递一个 Lambda,但我们传递属性引用 Message::isRead

当指定非平凡的排序顺序时,属性引用非常有用:

// MemberReferences/SortWith.kt
import memberreferences1.Message
import atomictest.eq

fun main() {
  val messages = listOf(
    Message("Kitty", "Hey!", true),
    Message("Kitty", "Where are you?", false),
    Message("Boss", "Meeting today", false))
  messages.sortedWith(compareBy(
    Message::isRead, Message::sender)) eq
    listOf(
      // 首先是未读消息,按发送者排序:
      Message("Boss", "Meeting today", false),
      Message("Kitty",
        "Where are you?", false),
      // 然后是已读消息,也按发送者排序:
      Message("Kitty", "Hey!", true))
}

库函数 sortedWith() 使用比较器对列表进行排序,比较器是用于比较两个元素的对象。库函数 compareBy() 基于其参数构建比较器,参数是一系列的谓词。使用具有单个参数的 compareBy() 等效于调用 sortedBy()

函数引用

假设您想要检查 List 是否包含任何重要消息,而不仅仅是未读消息。您可能有许多复杂的标准来决定“重要”是什么意思。您可以将此逻辑放入 Lambda 中,但该 Lambda 可能很容易变得又大又复杂。如果您将其提取到单独的函数中,代码会更容易理解。在 Kotlin 中,您不能将函数传递到函数类型的位置,但可以传递对该函数的引用

// MemberReferences/FunctionReference.kt
package memberreferences2
import atomictest.eq

data class Message(
  val sender: String,
  val text: String,
  val isRead: Boolean,
  val attachments: List<Attachment>
)

data class Attachment(
  val type: String,
  val name: String
)

fun Message.isImportant(): Boolean =
  text.contains("Salary increase") ||
    attachments.any {
      it.type == "image" &&
        it.name.contains("cat")
    }

fun main() {
  val messages = listOf(Message(
    "Boss", "Let's discuss goals " +
    "for next year", false,
    listOf(Attachment("image", "cute cats"))))
  messages.any(Message::isImportant) eq true
}

这个新的 Message 类添加了一个 attachments 属性,扩展函数 Message.isImportant() 使用了这个信息。在调用 messages.any() 时,我们创建了对扩展函数的引用 - 引用不限于成员函数。

如果有一个以 Message 作为唯一参数的顶级函数,您可以将其作为引用传递。当您创建对顶级函数的引用时,没有类名,因此写为 ::function

// MemberReferences/TopLevelFunctionRef.kt
package memberreferences2
import atomictest.eq

fun ignore(message: Message) =
  !message.isImportant() &&
    message.sender in setOf("Boss", "Mom")

fun main() {
  val text = "Let's discuss goals " +
    "for the next year"
  val msgs = listOf(
    Message("Boss", text, false, listOf()),
    Message("Boss", text, false, listOf(
      Attachment("image", "cute cats"))))
  msgs.filter(::ignore).size eq 1
  msgs.filterNot(::ignore).size eq 1
}

构造函数引用

您可以使用类名创建对构造函数的引用。

在这里,names.mapIndexed() 使用构造函数引用 ::Student

// MemberReferences/ConstructorReference.kt
package memberreferences3
import atomictest.eq

data class Student(
  val id: Int,
  val name: String
)

fun main() {
  val names = listOf("Alice", "Bob")
  val students =
    names.mapIndexed { index, name ->
      Student(index, name)
    }
  students eq listOf(Student(0, "Alice"),
    Student(1, "Bob"))
  names.mapIndexed(::Student) eq students
}

mapIndexed()Lambdas 中已介绍。它将 names 中的每个元素转换为该元素的索引以及元素本身。在 students 的定义中,这些被显式地映射到构造函数,但是使用 names.mapIndexed(::Student) 可以实现相同的效果。因此,函数和构造函数引用可以消除只是传递到 Lambda 中的一长串参数的需要。函数和构造函数引用通常比 Lambda 更具可读性。

扩展函数引用

要生成对扩展函数的引用,将引用前缀添加到扩展类型的名称:

// MemberReferences/ExtensionReference.kt
package memberreferences
import atomictest.eq

fun Int.times47() = times(47)

class Frog
fun Frog.speak() = "Ribbit!"

fun goInt(n: Int, g: (Int) -> Int) = g(n)

fun goFrog(frog:

 Frog, g: (Frog) -> String) =
  g(frog)

fun main() {
  goInt(12, Int::times47) eq 564
  goFrog(Frog(), Frog::speak) eq "Ribbit!"
}

goInt() 中,g 是一个期望 Int 参数并产生 Int 的函数。在 goFrog() 中,g 期望一个 Frog 并产生一个 String

练习和解答可在 www.AtomicKotlin.com 找到。