2015-08-16 2 views
2

Предположим, у меня есть экземпляр MethodMirror, созданный для определенного метода объекта. По полям зеркала я могу легко получить доступ к типу возвращаемого значения и параметрам метода. Но мне действительно нужно получить тип, который этот метод имел бы как функцию.Тип функции метода метода из экземпляра MethodMirror в Scala

Вот пример кода игрушки, который поможет мне объяснить, чего я хочу достичь. Я использую 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) => Int. Я бы предпочел решение, которое не слишком сильно зависит от конкретных классов Scala FunctionX.

+0

Что вы подразумеваете под «Я бы предпочел решение, которое не слишком сильно зависит от конкретных классов функций Scala»? '(String, String) => Int' - это просто другое имя для' Function2 [String, String, Int] ', они действительно то же самое (и, как таковое, ни один из них не более« конкретный », чем другой). –

+0

@ RégisJean-Gilles Я думаю, это означает, что можно построить тип функции из типа метода с помощью 'universe.appliedType' и передать ему' Function2', типы аргументов и тип возвращаемого значения, но OP хочет более общий путь создания eta-расширения для объектов типа. – Kolmar

+0

Ах да, вы правы, возможно, он имел в виду. Благодарю. –

ответ

3

Метод getEtaExpandedMethodType ниже выполняет то, что вы просили, и даже обрабатывает методы с несколькими списками параметров.

С другой стороны, он не обрабатывает общие методы. На примере def method[T](x: T) = 123, когда eta-extended, создает функцию типа Any => Int, но getEtaExpandedMethodType сообщит T => Int, который не только неверен, но и не имеет смысла вообще (T не имеет смысла в этом контексте).

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) 

тест РЕПЛ:

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 [email protected] 

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 
+0

Благодарим вас за столь универсальное решение. Для меня важна Btw, имеющая эти маркеры аргументов типа. Я читаю их из объявления метода и позже использую, чтобы определить, может ли этот метод использоваться в определенном контексте. Но эта 'compat._' вещь может устареть в будущих версиях Scala. Прежде чем я прочитал ваш ответ, я написал свой собственный код (после того, как я узнал, что 'applyType' был правильным инструментом в конце концов), который я публикую ниже. –

+0

Я не очень-то нашел использование 'compat._', но не смог найти новую альтернативу менее чем за одну минуту, поэтому я просто оставил ее как есть (документация по отражению уже очень разрежена, но когда она устарела, она становится реальная боль, чтобы найти то, что вам нужно). Я заменил 'typeRef'' applyType' в моем коде, работает как шарм. –

+0

Самое смешное, что теперь я понимаю, что Колмар уже указал мне на 'applyType' в своем комментарии. Дох, глупый. –

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) 
} 
+0

Функциональность 'arityToFunType' уже предоставляется стандартной библиотекой через' define.FunctionClass'.Это удаляет основную точку боли в вашем коде. –

+0

Справа. Спасибо, что указали это. Еще одна скрытая полезная утилита отражения Scala. Я улучшил свое решение, которое сейчас становится довольно коротким. –

Смежные вопросы