2017-04-17 6 views
2

私は、1つのメソッドを定義するインタフェースを作成しようとしています。そのメソッドの引数の型は、インタフェースの型パラメータでなければなりません。これは、インタフェースの実装がこのパラメータに対して一意の実装を持つためです。これまでのところ良いですが、catchには、メソッドに関連付けられた型引数もパラメータに含まれています。クラス型引数として定義された型のパラメータを持つことはできますか?また、型引数はメソッドに関連付けられていますか?Kotlinでクラスと関数のジェネリックを混在させる?

別の言い方をすれば:

私は2つの実装とAPIクライアント・インターフェースを構築しようとしています。各クライアントは、彼らのrxecute機能(RxJava +を実行して、はい、私は名前の不当誇りに思っています)に渡されRequest<T>オブジェクトに基づいてAPI呼び出しを行います。

私の問題は、インタフェースのrxecuteメソッドを定義する方法です。私はいくつかのサンプルコードをレイアウトしてみましょう(私の実際のコードはimplを使用していない、心配しないでください)。

interface Request<T> // T is return type of the request 

class RequestImpl<T>: Request<T> { // impl } 

interface ApiClient<R : Request<*>> { 
    // R should somehow be of type Request<T> 
    fun <T> rxecute(request: R): Single<T> 
} 

class ApiClientImpl : ApiClient<RequestImpl<????>> { 
    override fun <T> rxecute(request: RequestImpl<T>): Single<T> = // impl 
} 

私はこのようにそれを使用できるようにしたいと思います:

val apiClient = ApiClientImpl() 
apiClient.rxecute(RequestImpl<SomeClass>()).subscribe(someClassResult -> ...) 

それは、このようなインターフェイスを作成するか、ジェネリック医薬品は、そのように動作しない可能ですか?

答えて

3

残念ながら、Kotlinのジェネリクスはその方法で操作することはできません。 typeパラメータRは、ジェネリック型自体(R<T>)として使用することはできませんし、その上限を宣言サイトに固定され、そしてあなただけの別のものを追加するために、単一の上限(決して、where Q : R, Q : Request<T>としてtypeパラメータRを使用することができます動作しません)。あなたが唯一の具体的な種類をパラメータ化することができます

、そして私の心に来る一つの選択肢は、recursive genericsパターン(それはまた、Javaで使用されている)です。実装タイプの別の型パラメータをRequestに追加すると、interface Request<R, T>になります。これは、継承された型宣言への実装の種類を追加する必要があります。ここで生じた歓迎されない副作用は、あなたがキャストする必要があるということです

interface ApiClient<R : Request<R, *>> { 
    fun <T> rxecute(request: Request<R, T>): Single<T> 
} 

class RequestImpl<T>: Request<RequestImpl<*>, T> { /* impl */ } 

その後ApiClientを変更requestRequestImplrequest as RequestImplとして使用してください。

、次のようにApiClientImpl変換:

class ApiClientImpl : ApiClient<RequestImpl<*>> { 
    override fun <T> rxecute(request: Request<RequestImpl<*>, T>): Single<T> = TODO() 
} 

をより強力な型システムを必要とする、このソリューションは、はるかに詳細なあなたが尋ねたものよりも見えますが、再帰的なジェネリック医薬品は、このような場合のための共通の問題を回避するように見えますJavaとKotlinが提供できるもの


(runnable demo of the code in the answer)

+0

私は前に再帰的なジェネリックを見ていないが、それは絶対にbadshitです。さらに、それはコンパイルされます。私はすべてのことを実行する前にいくつかの他のコードを修正する必要がありますが、それは良いようです。 マイナー詳細;あなたは '(requestImplとしてrequestImpl )'をキャストする必要があります。これは少し醜いですが安全です。 私は私のものを修正し、それが動作していることを確認するとすぐにこれを答えてマークします。 –

+0

@DavidWhitman、yep、 'requestImplとしてのリクエスト'は、再帰ジェネリックを使用するときに頸部で知られている痛みです。あなたは型の安全性を失うことなくビットを短くすることができます: 'RequestImplとしてのリクエスト'( ''は実行時にチェックされず、コンパイル時に一致すれば安全です) – hotkey

+0

恒星の答え、コードは完璧に動作しています。 Kotlinは美しい言語です。あなたの仕事のために本当にありがとう。 私は回避策がある奇妙な残りのビットが1つありましたが、他の人は意識しておきたいことがあります。私は署名 'class ApiClientWrapper 、R:Request >'を持つ 'ApiClient'のラッパーを作成しました。あなたは結果を 'T'(' .map {it as T} ')にマップするために安全でないキャストを行う必要があります。これは、' fun rxecute(request:R):Single 'を使って' rxecute'呼び出しをラップします。 )なぜなら、 'request:R'は型パラメータ' Any? 'を持っているからです。 –

関連する問題