2

MethodMirrorオブジェクトの特定のメソッドに対して作成されたインスタンスがあるとします。ミラーのフィールドによって、メソッドの戻り値の型とパラメーターに簡単にアクセスできます。しかし、実際には、このメソッドが関数として持つ型を取得する必要があります。

これは、私が達成したいことを説明するのに役立つおもちゃのコード例です。Scala 2.11.6 を使用しています。

import scala.reflect.runtime.universe._

object ForStackOverflow {
  object Obj {
    def method(x:String, y:String):Int = 0
    def expectedRetType():((String, String) => Int) = ???
  }

  def main(args: Array[String]) {
    val mirror:Mirror = runtimeMirror(getClass.getClassLoader)
    val instanceMirror = mirror.reflect(Obj)

    val methodSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("method")).asMethod
    val methodMirror = instanceMirror.reflectMethod(methodSymbol)

    println(methodMirror.symbol.returnType)
    println(methodMirror.symbol.paramLists(0).map { x => x.info.resultType }.mkString(", "))

    val expectedSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("expectedRetType")).asMethod
    println("I would like to produce from a 'methodMirror' this: "+expectedSymbol.returnType)
  }
}

関数を表すTypeからインスタンスを生成したいと思います。methodMirrorこの例では、 である必要があります(String, String) => IntFunctionX具体的な Scala のクラスにあまり依存しないソリューションが望ましいと思います。

4

2 に答える 2

3

以下のメソッドgetEtaExpandedMethodTypeは、あなたが求めたことを実行し、複数のパラメーター リストを持つメソッドも処理します。

一方、ジェネリック メソッドは処理しません。たとえばdef method[T](x: T) = 123、イータ展開すると、タイプ の関数が作成されますが、Any => Intこれは正しくないだけでなく、まったく意味がないgetEtaExpandedMethodTypeと報告されます (このコンテキストでは意味がありません)。T => IntT

def getEtaExpandedMethodType(methodSymbol: MethodSymbol): Type = {
  val typ = methodSymbol.typeSignature
  def paramType(paramSymbol: Symbol): Type = {
    // TODO: handle the case where paramSymbol denotes a type parameter
    paramSymbol.typeSignatureIn(typ)
  }

  def rec(paramLists: List[List[Symbol]]): Type = {
    paramLists match {
      case Nil => methodSymbol.returnType
      case params :: otherParams =>
        val functionClassSymbol = definitions.FunctionClass(params.length)
        appliedType(functionClassSymbol, params.map(paramType) :+ rec(otherParams))
    }
  }
  if (methodSymbol.paramLists.isEmpty) { // No arg method
    appliedType(definitions.FunctionClass(0), List(methodSymbol.returnType))
  } else {
    rec(methodSymbol.paramLists)
  }
}
def getEtaExpandedMethodType(methodMirror: MethodMirror): Type = getEtaExpandedMethodType(methodMirror.symbol)

REPL テスト:

scala> val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
mirror: reflect.runtime.universe.Mirror = ...

scala> val instanceMirror = mirror.reflect(Obj)
instanceMirror: reflect.runtime.universe.InstanceMirror = instance mirror for Obj$@21b6e507

scala> val tpe = instanceMirror.symbol.toType
tpe: reflect.runtime.universe.Type = Obj.type

scala> getEtaExpandedMethodType(tpe.decl(TermName("method1")).asMethod)
res28: reflect.runtime.universe.Type = (String, String) => scala.Int

scala> getEtaExpandedMethodType(tpe.decl(TermName("method2")).asMethod)
res29: reflect.runtime.universe.Type = () => String

scala> getEtaExpandedMethodType(tpe.decl(TermName("method3")).asMethod)
res30: reflect.runtime.universe.Type = () => scala.Long

scala> getEtaExpandedMethodType(tpe.decl(TermName("method4")).asMethod)
res31: reflect.runtime.universe.Type = String => (scala.Float => scala.Double)

scala> getEtaExpandedMethodType(tpe.decl(TermName("method5")).asMethod)
res32: reflect.runtime.universe.Type = T => scala.Int

scala> getEtaExpandedMethodType(tpe.decl(TermName("method6")).asMethod)
res33: reflect.runtime.universe.Type = T => scala.Int
于 2015-08-20T13:24:19.597 に答える
2

これは、おそらく を使用した最も簡単なソリューションuniverse.appliedTypeです。複数のパラメーター リストの場合は機能しません。この問題を解決する別の方法を示すためにこれを投稿します。

def getEtaExpandedMethodType2(methodSymbol: MethodSymbol): Type  = {
  val typesList = methodSymbol.info.paramLists(0).map(x => x.typeSignature) :+ methodSymbol.returnType
  val arity = methodSymbol.paramLists(0).size
  universe.appliedType(definitions.FunctionClass(arity), typesList)
}
于 2015-08-20T20:11:28.753 に答える