2015-11-09 20 views
10

kotlinとラムダ/関数参照を使用しようとすると、私はコンパイルエラーに直面している:関数参照とラムダ

class Foo { 

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

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

} 

しかし、私は型の不一致を言って、コンパイル時のエラーを取得:

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

'::'の前に明示的に指定する必要があるエラーとそのタイプはわかりません

別の質問 kotliのリファレンスとして別のオブジェクト関数を使用できますかn?このようなもの:

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

答えて

12

2番目の例では、bound function referenceという構文がKotlin 1.1以降でサポートされているため、Javaと同様にbar::isOddと記述することができます。

最初の例では、エラーがisOddが実際に(タイプFooIntの)二つのパラメータの関数であることを言おうとしており、その種類つのパラメータの関数であり、引数として2つのパラメータを取る関数を渡し許可されていません。この例をコンパイルするには、isOddをトップレベル関数またはローカル関数にすることができます。これにより、Intという1つのパラメータの関数になります。または、Kotlin 1.1以降を使用する場合は、バインドされた関数参照構文を使用して、単にthis::isOddと記述します。

+0

テストのために便利でしょう。私がラムダを直接渡すと、ラムダそのものを単体テストする本当の方法はありません。したがって、私は関数を使う方法を探していました。なぜなら、関数自体は単体テスト可能であるからです。私が見ている限り、クラス内で関数を定義することもできます: 'class Foo {val isOdd = fun(i:Int):Boolean {return i%2!= 0}}'。クラスFooでは、ラムダの代わりに 'isOdd'を渡すことができ、isOddはまだユニットテスト可能です。しかし、これは回避策のようです。 Java 8と同様の関数参照を単体テストのために使ってもいいですか? – sockeqwe

+0

@udalovローカル関数の意味は?どのように作成するのですか?ラムダによって? – voddan

+0

ローカル関数は、別の関数内で宣言された関数です。https://kotlinlang.org/docs/reference/functions.html#local-functionsを参照してください。 –

5

これは面白いです。 "Java strikes back"。ハハ

あなたの問題は簡単です:あなたはisOddFooというクラスで宣言しましたか?次にファンクションではなく、メソッドです。これは、Fooのインスタンス(this参照)を渡す必要があることを意味します。そのため、2つのパラメータ:Foo.(Int) -> Booleanの関数です。構文エラーは、メソッドへの参照がFoo::isOddのように見えることを示しています。

とにかく、オブジェクトを使用しない非静的メソッドを宣言することは、Javaでも反パターンです。

問題は、クラスなしか拡張することによって、自由な関数を宣言することによって解決される可能性があります:fun Int.isOdd()

P.S.あなたの2番目の質問については、その機能はまだサポートされていません。

+0

私は反パターンについて同意しなければなりません。 isOdd()はオーバーライド可能なメソッドであり、クラスはそれを使用してテンプレートパターンを実装できます。 Javaはそのようなメソッド参照をうまくサポートしています。私はKotlinがそれをサポートすべきだと思います。純粋なJava 8から来たときには、これを一歩前にしていると思います。Kotlinで定義された機能インタフェースを実装するためにラムダを使用できないのと同じことです:物事に名前を付けることができないということは、 JavaインターフェイスとKotlinインターフェイスの違いは奇妙です。 –

+1

@nizet OK、あなたのポイントがあります。私はkotlinが機能的な参照で少し奇妙であることに同意します。誰かが言ったように、機能は一流の市民ではありませんが、ラムダはあります。私はKotlinがJava以上にサポートしなければならないと信じています – voddan

+0

Kotlinの機能は一流の市民です。ここで問題となるのは、2つのパラメータ(receiver、param0)を持つ関数は、1つのパラメータのみを必要とする関数の代わりに使用できないということです。あなたが探しているのは、レシーバへの参照をコールに渡すことでメソッドを関数に変えるメソッドクロージャです。コードは、クラスメソッドを呼び出すラムダを渡すことで簡単にこれを行うことができます。それは受信者としてのクラスのインスタンスで終了します。その後、Kotlinはこれを自動的に行うかもしれません。 –