2

特殊なフィールドを持ち、生のデータ型を使用しているクラスがあります。たとえば、Tuple2 [Int、String]:

scala> class TupleReflection(val tuple: Tuple2[Int, String])
defined class TupleReflection

scala> val refl = new TupleReflection((5, "hello"))
refl: TupleReflection = TupleReflection@1a597ec8   

ここでリフレクションを使用して、「refl」インスタンス内のTuple2の型パラメーターを見つけたいと思います。(フィールドを取得するために「head」を使用して少しごまかします。これが唯一のフィールドであることがわかっているためです。)

scala> val field = refl.getClass.getDeclaredFields.head
field: java.lang.reflect.Field = private final scala.Tuple2 TupleReflection.tuple

これでフィールドができたので、ジェネリック型を照会できます。

scala> field.getGenericType
res41: java.lang.reflect.Type = scala.Tuple2<java.lang.Object, java.lang.String>

ここでの問題は、最初のタイプがオブジェクトであるということです。反射だけで、そのパラメーターの実際の型(Int)を知る方法はありますか?

アップデート:

私はこれを自分のAPI内の自動シリアル化のコンテキストで使用しています。@Serializableでマークされたクラスが与えられた場合、それをシリアル化できます。そのためには、リフレクションを使用してクラスのフィールドとタイプのツリーを再帰的に構築し、深いシリアル化を実行できるようにする必要があります。

@Specializedクラスを直接操作している場合、型は明示的であり、呼び出しサイトでのコンパイル時に認識されているため、機能します。階層内のフィールドが@specializedの場合、リフレクションを介して判断する方法はありません。クラスで宣言されたフィールドまたはメソッドをクエリしても、正しい値は得られません。タイプは実行時に存在しますが、フィールド自体の宣言ではなく、フィールドに保持されているインスタンスにのみ存在します。したがって、インスタンスがnullで、「getClass」を実行できない場合、リフレクションだけでは正しいタイプを知ることができません。

4

2 に答える 2

5

問題は、JVMの「型消去」の問題を回避しないJavaリフレクションAPIを使用していることです。これは、実際の汎用型を見つける方法がないためです。

幸い、Scalaの次の2.10バージョンは、型消去の問題を解決する新しいリフレクションAPIを実装しています。ただし、2.10はまだリリースされていないため、APIはまだ標準化も文書化もされていません。最善の策は、デバッガーなどのツールを使用してそれを掘り下げ、ここで出てくるより具体的な質問をすることです。

Scala 2.10-M5では、次のようにAPIにアクセスできます。

scala> reflect.runtime.universe.typeOf[(Int, String)]
res0: reflect.runtime.universe.Type = (Int, String)

scala> reflect.runtime.universe.typeOf[(Int, String)].typeArguments
res1: List[reflect.runtime.universe.Type] = List(Int, String)

scala> reflect.runtime.universe.typeOf[(Int, String)].typeArguments.head
res2: reflect.runtime.universe.Type = Int

アップデート#1

次の関数は、インスタンスのタイプを取得する方法を示しています。

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> def typeOf[T : TypeTag](x : T) = reflect.runtime.universe.typeOf[T]
typeOf: [T](x: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type

scala> typeOf(Seq((1,"sldf"),(20,"sldkfjew")))
res0: reflect.runtime.universe.Type = Seq[(Int, String)]

実際、それはすべて[T : TypeTag]、渡された型へのリフレクションの暗黙のインスタンスを魔法のように作成するようにコンパイラーに指示する部分に基づいています。

アップデート#2

scala> class TupleReflection(val tuple: Tuple2[Int, String])
defined class TupleReflection

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> typeOf[TupleReflection].member(newTermName("tuple")).typeSignature
res6: reflect.runtime.universe.Type = => (scala.Int, String)

scala> typeOf[TupleReflection].members
res7: Iterable[reflect.runtime.universe.Symbol] = List(constructor TupleReflection, value tuple, value tuple, method $asInstanceOf, method $isInstanceOf, method synchronized, method ##, method !=, method ==, method ne, method eq, constructor Object, method notifyAll, method notify, method clone, method getClass, method hashCode, method toString, method equals, method wait, method wait, method wait, method finalize, method asInstanceOf, method isInstanceOf, method !=, method ==)

scala> typeOf[TupleReflection].members.view.filter(_.isValue).filter(!_.isMethod).toList
res16: List[reflect.runtime.universe.Symbol] = List(value tuple)
于 2012-07-20T21:21:51.137 に答える
0

私は方法を見つけましたが、それはきれいではありません。

class TupleReflection(@(specializedFor @field)(Array(classOf[Int], classOf[String]) val tuple: Tuple2[Int, String])

ランタイム保持ポリシーを使用してspecializedForアノテーションを作成しました。Class[_]の配列を受け取ります。そうすれば、実行時に、Tuple2フィールドのタイプを反映して排他的に見つけることができます。

配列にTuple2と同じ型が含まれていることをテストできないため、安全ではありません。

私のAPIでは、最初にアノテーションが存在するかどうか、そしてそれがgenericTypesを強制的にそれらにするかどうかをチェックする必要があります。

于 2012-07-21T02:09:31.830 に答える