Kotlin: làm thế nào để truyền một hàm làm tham số cho một hàm khác?


140

Cho hàm foo:

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

Chúng tôi có thể làm:

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

Bây giờ, giả sử chúng ta có chức năng sau:

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

Có cách nào để tôi có thể chuyển "buz" làm tham số cho "foo" không? Cái gì đó như:

foo("a message", buz)

Câu trả lời:


198

Sử dụng ::để biểu thị một tham chiếu hàm, và sau đó:

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)
}

Vì Kotlin 1.1, giờ đây bạn có thể sử dụng các hàm là thành viên của lớp (" Tham chiếu có thể gọi được giới hạn "), bằng cách thêm tiền tố vào toán tử tham chiếu hàm với ví dụ:

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

foo("hi", thatOtherThing::buz)

foo("hi", this::buz)

1
vui lòng sửa lại cho tôi nếu tôi sai nhưng dường như chỉ các hàm cấp cao nhất (nghĩa là không thuộc về một lớp) có thể được chuyển theo cách này; phương thức lớp học không thể :-(
Jaja Harris

7
Một tham chiếu thành viên có thể được truyền xung quanh nhưng đó sẽ là hàm 2 tham số trong trường hợp này với tham số đầu tiên yêu cầu một thể hiện của lớp. Một cách tốt hơn là bọc chức năng thành viên bằng lambda đã đóng trên lớp. Giả sử tất cả những điều trên là trong một lớp: fun something() { foo("hi", { buz(it) }) }
Jayson Minard

1
Dường như các chức năng thuộc về một lớp có thể được truyền vào nếu bạn đang hoạt động trong cùng một lớp, kể từ kotlin 1.1, bạn có thể thực hiện vớithis::function
Joe Maher

1
Ngoài ra, nếu bạn muốn làm cho tham số hàm trở thành null, chỉ cần bọc khai báo kiểu trong ngoặc đơn bằng dấu chấm hỏi ở cuối. ví dụbar: ((m: String) -> Unit)?
Aba

1
@MartyMiller họ không có. Tham số mnày dành cho foo, tham số khác là tên tham số của loại chức năng được truyền trong thanh biến cho hàm.
Jayson Minard

12

Về chức năng thành viên như tham số:

  1. Lớp Kotlin không hỗ trợ chức năng thành viên tĩnh, vì vậy chức năng thành viên không thể được gọi như: Toán tử :: add (5, 4)
  2. Do đó, chức năng thành viên không thể được sử dụng giống như chức năng hạng nhất.
  3. Một cách tiếp cận hữu ích là bọc chức năng với lambda. Nó không thanh lịch nhưng ít nhất nó đang hoạt động.

mã:

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
Trong Kotlin hiện tại, bây giờ bạn có thể sử dụng chức năng thành viên làm tài liệu tham khảo. Bây giờ bạn nên cập nhật câu trả lời này.
Jayson Minard

Bằng cách xác định các hàm trong mã gọi đối tượng đồng hành sẽ tốt hơn một chút và không có phiên bản mới nào của Toán tử được tạo mỗi lần. Điều này sẽ trông giống như một niềm vui tĩnh trong Java
CAB

Giải pháp tuyệt vời, đây là cách duy nhất tôi thấy hoạt động khi hàm nằm trong một lớp. Tôi không coi nó xấu nhưng đẹp, gần giống như một biến mẫu góc cạnh nhưng là mã.
gunlingor


4

Chỉ cần sử dụng "::" trước tên phương thức

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

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

foo(::bar) Đầu ra :Hello World


mã này không biên dịch. "function ()" yêu cầu một tham số
Giuseppe Giacoppo


1

Nếu bạn muốn vượt qua các phương thức settergetter .

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

Sử dụng:

private var width: Int = 1

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

1

Câu trả lời của Jason Minard là một câu hỏi hay. Điều này cũng có thể đạt được bằng cách sử dụng a lambda.

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

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

Mà có thể được gọi với foo("a message", buz).

Bạn cũng có thể làm cho DRY thêm một chút bằng cách sử dụng a typealias.

typealias qux = (m: String) -> Unit

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

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

0

Một vi dụ khac:

 fun foo(x:Int, Multiply: (Int) -> (Int)) {
    println(Multiply(x))
 }
 fun bar(x:Int):Int{
    return  x * x
 }
 foo(10, ::bar)

-7

Các chức năng hạng nhất hiện không được hỗ trợ trong Kotlin. Đã có tranh luận về việc liệu đây sẽ là một tính năng tốt để thêm vào. Cá nhân tôi nghĩ rằng họ nên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.