2015-08-13 3 views
2

Я пытаюсь вызвать функцию с помощью Reflection API SCALA в v2.11.6:Scala Reflection: вызов метода применения Function1 - несколько альтернатив?

import scala.reflect.runtime.{universe => ru} 

def f(i: Int) = i + 2 

val fn: Any = (f _) 
val ref = ru.runtimeMirror(ru.getClass.getClassLoader).reflect(fn) 
val apply = ref.symbol.typeSignature.member(ru.TermName("apply")) 

При использовании ref.reflectMethod(apply.asMethod) он жалуется на несколько альтернатив apply на ref. В исследовании apply.asTerm.alternatives показаны два метода: один с сигнатурой (x$1: Int)Int, а другой - (v1: T1)R. Вызов

ref.reflectMethod(apply.asTerm.alternatives(1).asInstanceOf[ru.MethodSymbol])(1) 

(со вторым вариантом) возвращает правильный результат (3). Однако при вызове первой альтернативы возникает исключение: java.lang.IllegalArgumentException: object is not an instance of declaring class

Каковы эти альтернативы и как я могу убедиться, что всегда вызываю правильный? Также существует проблема с вызовом Function2 или выше с помощью этого метода, так что это правильный способ сделать это?

ответ

2

Причина, по которой перегружены методы apply, заключается в том, что Function1 является @specialized для примитивных типов.

Я не знаю, если есть лучший способ, чтобы отличить их, но следующий, кажется, работает, ищет альтернативы которой аргумент стирает в AnyRef (вместо примитивного, таких как Int):

def invoke(fun1: Any, arg1: Any): Any = { 
    import scala.reflect.runtime.{universe => ru} 
    val mirror = ru.runtimeMirror(ru.getClass.getClassLoader) 
    val ref  = mirror.reflect(fn) 
    val applies = ref.symbol.typeSignature.member(ru.TermName("apply")) 
    val syms = apply.alternatives.map(_.asMethod) 
    val sym  = syms.find { m => 
    m.paramLists match { 
     case (arg :: Nil) :: Nil 
     if arg.asTerm.typeSignature.erasure =:= ru.typeOf[AnyRef] => true 
     case _ => false 
    } 
    } getOrElse sys.error("No generic apply method found") 
    ref.reflectMethod(sym)(arg1) 
} 

// Test: 
val fn: Any = (i: Int) => i + 2 
invoke(fn, 1) // res: 3 
+0

Я полагаю, что '@ unspecialized' должен работать так же – Pyetras

+0

@Pyetras - отлично, не знал, что существует. –

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