2016-03-15 17 views
5

タイプパラメータを指定せずにメソッドを呼び出すサブクラスを持つ汎用パラメータを持つ抽象クラスを作成しようとしています。 私はこれまでのところ、これを持っている:汎用パラメータを持つ抽象クラスと型パラメータを使用するメソッド

abstract class AbstractClass<T : Any> @Autowired constructor(protected val delegate: MyService) { 
    inline fun <T: Any> myMethod(param: Any): T? { 
     return delegate.myMethod(param).`as`(T::class.java) 
    } 
} 

と実装:私は型引数を指定する必要がmyMethodを呼び出すとき

class TesterWork @Autowired constructor(delegate: MyService) : AbstractClass<Tester>(delegate) { 
} 

は今:

testerWork.myMethod<Tester>("test") 

私はそれが可能だろうと思いまして型引数を自動的に推論するか? 私は何とかmyMethodを再加工できますか?メソッド内にT::class.javaが必要であることに注意してください。

答えて

8

実行時にジェネリックパラメータが消去されるため、クラスのジェネリックパラメータをに設定することはできません(T::classトークンの取得)。コトルはJava's type erasure practiceに準拠しています。
それはあなたがそれを使用できるようにClass<T>トークンを渡し、保存するのはあなた次第です、ということを考えると

(Kotlinはインライン関数のためのreified genericsを持っています)。

また、あなたの例ではmyFunctionは、一般的なパラメータを導入し、そしてそれがどのような方法でクラスジェネリックパラメータに接続されていない、新しい汎用になります(両方Tを命名するだけ混乱を追加し、それらT1T2考えます)。私が正しく理解すれば、代わりにジェネリッククラスを意味します。 classTokenオーバーライドが必要になりますAbstractClassから派生する、そして、

abstract class AbstractClass<T : Any> constructor(protected val delegate: MyService) { 
    protected abstract val classToken: Class<T> 

    fun myMethod(param: Any): T? { 
     return delegate.myMethod(param).`as`(classToken) 
    } 
} 

は、おそらくあなたが何ができるか、それが保存されたクラスのトークンを使用するように、トークンクラスを格納し、機能を書き換えるだろう抽象valを宣言しています:

class TesterWork constructor(delegate: MyService) : AbstractClass<Tester>(delegate) { 
    override val classToken = Tester::class.java 
} 

その後、あなたは、一般的なパラメータを指定せずにTesterWorkインスタンス上の関数を呼び出すことができるようになります。

val service: MyService = ... 
val t: Tester? = TesterWork(service).myMethod("test") 
+0

ありがとうございました!私は、なぜジェネリックparamをメソッドgenericパラムに接続できないのか、少し困惑しています。サブクラスが作成されると、ジェネリック型がわかります。なぜ抽象クラスの "T型"として使用できないのですか?なぜ型を指定しなければならないのでしょうか? – NikolaB

+0

@ NikolaB、**両方のクラスとそのメソッドにジェネリックパラメータを追加すると、それらは互いに接続されていない別のパラメータになります。それ以外の場合は、関数内でクラスのgenericパラメータを使用することができます。抽象クラスに関しては、JVMのジェネリックスは異なる方法で動作します。メソッドコードは派生クラスごとに複製されないため、派生クラスのジェネリックパラメータの代わりに具体的な型を指定することは役に立ちません。メソッドのコードはそれでも基本クラスでは、ジェネリックはそれから消去されます。 – hotkey

+0

@NikolaB、おそらく読んでいる* https://en.wikipedia.org/wiki/Generics_in_Javaのタイプ消去のセクション*の問題は、物事をより明確にするでしょう。 – hotkey

3

ここにはいくつか問題があります。まず、ジェネリック型のパラメータからクラスオブジェクトを取り出すためには、それを証明する必要があります。このためには、<reified T: Any>と宣言する必要があります。

第2に、ジェネリック型パラメータを2回宣言します。クラス宣言abstract class AbstractClass<T : Any>に一度、メソッド宣言inline fun <T: Any> myMethodに1回。それらのTは同じではありません。理論的には、メソッドシグネチャから1つを除外することはできますが、これは機能しません。これは、Reificationがクラスではなくインラインメソッドでのみ機能するためです。可能な解決策は、@ hotkeyの答えを参照してください。あなたは抽象クラスにキャストラムダ与えることができます

1

abstract class AbstractClass<T : Any>(val delegate: MyService, val castFn: (Any) -> T) { 
    fun myMethod(param: Any): T? { 
     return castFn(delegate.myMethod(param)) 
    } 
} 

class TesterWork(delegate: MyService) : AbstractClass<Tester>(delegate, {it as Tester}) { 

} 

をあなたは多分また、クラスは非抽象作るそれをインターフェースを与え、インライン関数は、それを作成してみましょうことができます:

interface Inter<T> { 
    fun myMethod(param: Any): T 
} 

class NotAbstractClass<T>(val delegate: MyService, val castFn: (Any) -> T) : Inter<T> { 
    override fun myMethod(param: Any): T { 
     return castFn(delegate.myMethod(param)) 
    } 
} 

inline fun <reified T> makeClass(delegate: MyService): Inter<T> { 
    return NotAbstractClass<T>(delegate, { it as T }) 
} 

次に、このように委任することができます:

class TesterWork(delegate: MyService) : Inter<Tester> by makeClass(delegate) 

val service: MyService = MyService() 
val t: Tester? = TesterWork(service).myMethod("test") 
関連する問題