Kotlin: จะส่งฟังก์ชันเป็นพารามิเตอร์ไปยังอีกฟังก์ชันหนึ่งได้อย่างไร


157

ให้ฟังก์ชัน foo:

fun foo(m: String, bar: (m: String) -> Unit) {
    bar(m)
}

เราสามารถทำได้:

foo("a message", { println("this is a message: $it") } )
//or 
foo("a message")  { println("this is a message: $it") }

ตอนนี้สมมติว่าเรามีฟังก์ชันต่อไปนี้:

fun buz(m: String) {
   println("another message: $m")
}

มีวิธีส่ง "buz" เป็นพารามิเตอร์ไปยัง "foo" ได้อย่างไร สิ่งที่ต้องการ:

foo("a message", buz)

คำตอบ:


230

ใช้::เพื่อแสดงการอ้างอิงฟังก์ชันจากนั้น:

fun foo(m: String, bar: (m: String) -> Unit) {
    bar(m)
}

// my function to pass into the other
fun buz(m: String) {
    println("another message: $m")
}

// someone passing buz into foo
fun something() {
    foo("hi", ::buz)
}

เนื่องจาก Kotlin 1.1คุณสามารถใช้ฟังก์ชันที่เป็นสมาชิกคลาสได้แล้ว (" Bound Callable References ") โดยนำหน้าตัวดำเนินการอ้างอิงฟังก์ชันด้วยอินสแตนซ์:

foo("hi", OtherClass()::buz)

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)

1
โปรดแก้ไขฉันหากฉันผิด แต่ดูเหมือนว่าจะมีเพียงฟังก์ชันระดับบนสุด (เช่นไม่ได้เป็นของคลาส) เท่านั้นที่สามารถส่งผ่านได้ วิธีการเรียนไม่สามารถ :-(
Jaja Harris

8
การอ้างอิงสมาชิกสามารถส่งผ่านไปรอบ ๆ ได้ แต่จะเป็นฟังก์ชันพารามิเตอร์ 2 ในกรณีนี้โดยพารามิเตอร์แรกที่ต้องการอินสแตนซ์ของคลาส วิธีที่ดีกว่าคือการรวมฟังก์ชันสมาชิกด้วยแลมด้าที่ปิดคลาส สมมติว่าทั้งหมดข้างต้นอยู่ในชั้นเรียน: fun something() { foo("hi", { buz(it) }) }
Jayson Minard

1
ดูเหมือนว่าฟังก์ชันที่เป็นของคลาสสามารถส่งผ่านได้หากคุณใช้งานในคลาสเดียวกันเช่นเดียวกับ kotlin 1.1 คุณสามารถทำได้ด้วยthis::function
Joe Maher

1
นอกจากนี้หากคุณต้องการทำให้พารามิเตอร์ของฟังก์ชันเป็นโมฆะให้ห่อคำประกาศประเภทไว้ในวงเล็บโดยมีเครื่องหมายคำถามต่อท้าย เช่นbar: ((m: String) -> Unit)?
Aba

1
@MartyMiller พวกเขาไม่ได้ พารามิเตอร์mมีไว้สำหรับ foo พารามิเตอร์อื่นคือชื่อพารามิเตอร์ของประเภทฟังก์ชันที่ส่งผ่านในแถบตัวแปรไปยังฟังก์ชัน
Jayson Minard

13

เกี่ยวกับฟังก์ชันสมาชิกเป็นพารามิเตอร์:

  1. คลาส Kotlin ไม่รองรับฟังก์ชันสมาชิกแบบคงที่ดังนั้นจึงไม่สามารถเรียกใช้ฟังก์ชันสมาชิกได้เช่น: Operator :: add (5, 4)
  2. ดังนั้นจึงไม่สามารถใช้ฟังก์ชันสมาชิกเหมือนกับฟังก์ชันชั้นหนึ่งได้
  3. วิธีการที่มีประโยชน์คือการรวมฟังก์ชันด้วยแลมด้า ไม่สวยหรู แต่อย่างน้อยก็ใช้งานได้

รหัส:

class Operator {
    fun add(a: Int, b: Int) = a + b
    fun inc(a: Int) = a + 1
}

fun calc(a: Int, b: Int, opr: (Int, Int) -> Int) = opr(a, b)
fun calc(a: Int, opr: (Int) -> Int) = opr(a)

fun main(args: Array<String>) {
    calc(1, 2, { a, b -> Operator().add(a, b) })
    calc(1, { Operator().inc(it) })
}

4
ใน Kotlin ปัจจุบันคุณสามารถใช้ฟังก์ชันสมาชิกเป็นข้อมูลอ้างอิงได้แล้ว ตอนนี้คุณควรอัปเดตคำตอบนี้
Jayson Minard

การกำหนดฟังก์ชันในโค้ดการเรียกอ็อบเจ็กต์ร่วมจะดีขึ้นเล็กน้อยและไม่มีการสร้างอินสแตนซ์ใหม่ของ Operator ทุกครั้ง สิ่งนี้จะดูเหมือนความสนุกแบบคงที่ใน Java
CAB

ทางออกที่ดีนี่เป็นวิธีเดียวที่ฉันพบว่าใช้งานได้เมื่อฟังก์ชันอยู่ในคลาส ฉันไม่คิดว่ามันน่าเกลียด แต่สวยงามเกือบจะดูเหมือนตัวแปรเทมเพลตเชิงมุม แต่สำหรับโค้ด
gunlingor

6

เพียงใช้ "::" ก่อนชื่อเมธอด

fun foo(function: () -> (Unit)) {
   function()
}

fun bar() {
    println("Hello World")
}

foo(::bar) เอาท์พุต :Hello World


รหัสนี้ไม่ได้รวบรวม "function ()" ต้องการพารามิเตอร์
Giuseppe Giacoppo


2

หากคุณต้องการผ่านวิธีการsetterและgetter

private fun setData(setValue: (Int) -> Unit, getValue: () -> (Int)) {
    val oldValue = getValue()
    val newValue = oldValue * 2
    setValue(newValue)
}

การใช้งาน:

private var width: Int = 1

setData({ width = it }, { width })


1

คำตอบของ Jason Minard นั้นดี สิ่งนี้สามารถทำได้โดยใช้ไฟล์lambda.

fun foo(m: String, bar: (m: String) -> Unit) {
    bar(m)
}

val buz = { m: String ->
    println("another message: $m")
}

foo("a message", buz)ซึ่งสามารถที่จะเรียกว่ามี

คุณยังสามารถทำให้แห้งมากขึ้นอีกเล็กน้อยโดยใช้ไฟล์ typealias .

typealias qux = (m: String) -> Unit

fun foo(m: String, bar: qux) {
    bar(m)
}

val buz: qux = { m ->
    println("another message: $m")
}


0

คุณยังสามารถทำอินไลน์ได้โดยใช้แลมบ์ดาหากนั่นคือที่เดียวที่คุณใช้ฟังก์ชันนั้น

fun foo(m: String, bar: (m: String) -> Unit) {
    bar(m)
}

foo("a message") {
    m: String -> println("another message: $m")
}
//Outputs: another message: a message

-7

ขณะนี้ Kotlin ยังไม่รองรับฟังก์ชันชั้นหนึ่ง มีการถกเถียงกันว่านี่จะเป็นคุณสมบัติที่ดีที่จะเพิ่มหรือไม่ ส่วนตัวคิดว่าควร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.