2015-11-09 23 views
10

Tôi đang phải đối mặt với một lỗi biên dịch khi cố gắng sử dụng lambdas/tài liệu tham khảo chức năng với Kotlin:Chức năng Tài liệu tham khảo và lambdas

class Foo { 

    fun getFilteredList(){ 
     val numbers = listOf(1, 2, 3) 
     numbers.filter(::isOdd) // prints [1, 3] 
    } 

    fun isOdd(x: Int): Boolean = x % 2 != 0 

} 

Nhưng tôi nhận được một lỗi thời gian biên dịch nói một kiểu phù hợp:

Error:(18, 16) Gradle: Type inference failed: inline fun kotlin.Iterable.filter(predicate: (T) -> kotlin.Boolean): kotlin.List cannot be applied to receiver: kotlin.List arguments: (kotlin.reflect.KFunction2) Error:(18, 23) Gradle: Type mismatch: inferred type is kotlin.reflect.KFunction2 but (kotlin.Int) -> ??? was expected Error:(18, 23) Gradle: Type mismatch: inferred type is kotlin.reflect.KFunction2 but (kotlin.Int) -> kotlin.Boolean was expected Error:(18, 25) Gradle: Left-hand side of a callable reference with a receiver parameter cannot be empty. Please specify the type of the receiver before '::' explicitly

Tôi không chắc chắn lỗi nào cũng như loại tôi nên chỉ định rõ ràng trước '::'

Câu hỏi khác: Tôi có thể sử dụng một đối tượng khác làm tham chiếu trong kotli không n? Một cái gì đó như thế này:

class Bar { 
    fun isOdd(x: Int): Boolean = x % 2 != 0 
} 

class Foo { 

    fun getFilteredList(){ 
     val bar = Bar() 
     val numbers = listOf(1, 2, 3) 
     numbers.filter(bar::isOdd) // Use Bar's method 
    } 
} 

Trả lời

12

Trên ví dụ thứ hai: có, cú pháp bound function reference được hỗ trợ từ Kotlin 1.1, vì vậy bạn có thể viết bar::isOdd tương tự như Java. Trong ví dụ đầu tiên, lỗi đang cố gắng nói rằng isOdd thực chất là một hàm của hai tham số (loại FooInt) và chuyển một hàm lấy hai tham số làm đối số có kiểu là hàm của một tham số không được phép. Để biên dịch ví dụ, bạn có thể làm cho hàm isOdd ở mức cao nhất hoặc hàm cục bộ, làm cho hàm này trở thành một hàm của một tham số kiểu Int. Hoặc, nếu bạn sử dụng Kotlin 1.1+, hãy sử dụng cú pháp tham chiếu hàm ràng buộc và chỉ cần viết this::isOdd.

+0

Sẽ tiện dụng, bởi vì sự thử thách. Nếu tôi vượt qua một lambda trực tiếp không có cách nào thực sự để đơn vị kiểm tra lambda chính nó. Do đó tôi đang tìm cách sử dụng một hàm, vì bản thân hàm là đơn vị có thể kiểm thử được. Theo tôi thấy bạn cũng có thể định nghĩa một hàm trong một lớp như sau: 'class Foo {val isOdd = fun (i: Int): Boolean {return i% 2! = 0}}'. Sau đó, trong lớp Foo bạn có thể chuyển 'isOdd' thay vì lambda và isOdd vẫn sẽ là unit testable. Tuy nhiên, điều này có vẻ là một cách giải quyết. Sẽ được khá tốt đẹp để có tài liệu tham khảo chức năng tương tự như java 8 chỉ để thử nghiệm đơn vị – sockeqwe

+0

@udalov Những gì bạn có nghĩa là bởi một chức năng địa phương? Làm thế nào để bạn tạo ra một? Bởi một lambda? – voddan

+0

Chức năng cục bộ là chức năng được khai báo trong một hàm khác, xem https://kotlinlang.org/docs/reference/functions.html#local-functions –

5

Điều đó thật buồn cười. "Java tấn công trở lại". Haha

Vấn đề của bạn rất đơn giản: bạn đã khai báo isOdd trong lớp Foo, phải không? Sau đó, nó không phải là một hàm , nhưng phương thức . Điều đó có nghĩa là nó yêu cầu một phiên bản Foo để được chuyển vào (tham chiếu this) - đó là lý do tại sao nó là một chức năng của 2 tham số: Foo.(Int) -> Boolean. Và lỗi cú pháp cho thấy rằng - tham chiếu đến một phương thức trông giống như Foo::isOdd.

Dù sao, việc khai báo một phương pháp không tĩnh không sử dụng đối tượng là một mẫu mã ngay cả trong Java, bạn có đồng ý không?

Vấn đề có thể được giải quyết bằng cách tuyên bố một chức năng miễn phí mà không cần một lớp hoặc bằng cách làm cho nó trở thành một phần mở rộng: fun Int.isOdd()

T.B. Về câu hỏi thứ hai của bạn - tính năng đó chưa được hỗ trợ.

+0

Tôi phải không đồng ý về chống mẫu. isOdd() có thể là một phương thức overridable, và lớp có thể sử dụng nó để thực hiện mẫu khuôn mẫu. Java hỗ trợ các tham chiếu phương thức như vậy tốt, và tôi nghĩ Kotlin nên hỗ trợ nó. Tôi thấy rằng như một bước trở lại khi đến từ Java thuần 8. Giống như không thể sử dụng một lambda để thực hiện một giao diện chức năng được định nghĩa trong Kotlin: không thể đặt tên cho mọi thứ là một bước lùi với tôi. Và sự khác biệt giữa giao diện Java và giao diện Kotlin là kỳ quái. –

+1

@nizet OK, bạn có quan điểm của mình. Tôi đồng ý rằng kotlin là một chút lạ với các tài liệu tham khảo chức năng. Như ai đó đã nói, chức năng không phải là công dân hạng nhất, nhưng lambdas là. Tôi tin rằng Kotlin phải hỗ trợ không ít hơn Java – voddan

+0

Chức năng trong Kotlin là công dân hạng nhất. Vấn đề ở đây là một hàm có hai tham số (receiver, param0) không thể được sử dụng thay cho một tham số mà chỉ mong đợi một tham số. Những gì bạn đang tìm kiếm có một phương pháp đóng cửa, mà có thể biến một phương pháp thành một chức năng bằng cách thực hiện các tham chiếu đến người nhận vào cuộc gọi.Các mã có thể dễ dàng làm điều này bằng cách đi qua trong một lambda gọi phương thức lớp. Điều đó sẽ đóng trên trường hợp của lớp như người nhận. Sau đó Kotlin có thể làm điều này tự động. –

Các vấn đề liên quan