class Test(a: String, b: Array[String], c: Array[String]){
def this(b: Array[String], c: Array[String]) {
this("1", b, c)
}
def this() = {
this(null, null, null)
}
}
У меня есть класс Test, как выше, и я хотел бы использовать отражение Scala, чтобы вызвать один из них
Я пытаюсь использовать следующий код
import scala.reflect.runtime.{universe => ru}
val clsTest = ru.typeOf[Test].typeSymbol.asClass
val cm = m.reflectClass(clsTest)
val ctor = ru.typeOf[Test].decl(ru.termNames.CONSTRUCTOR).asTerm.alternatives.map(_.asMethod)
но я не знаю, как выбрать метод на основе сигнатуры метода. Есть ли какой-либо подход к выбору метода, основанного на сигнатуре типа, как Java отражает код? Спасибо!
Я прочитал документ Scala о отражении, но это не решает мою проблему. у него есть только один метод конструктора. Скала отражает документ
Всего 2 ответа
// the method as List(list(a,b,c))
// and this is PrimaryConstructor
class Test(a: String, b: Array[String], c: Array[String]) {
// the method as List(list(b,c))
def this(b: Array[String], c: Array[String]) {
this("1", b, c)
}
// the method as List(list())
def this() = {
this(null, null, null)
}
// the method as List(list(a),list(b,c)
def this(a:String )(b:String,c:String ){
this(null,null,null)
}
}
val constructor = typeOf[Test].members
// filter all constructor
.filter(e => e.isConstructor).map(e => e.asMethod)
// find which are you want
// edit 1
.find( e =>{
val methodParamsType = e.paramLists.head.map(e =>e.typeSignature)
// what params type are you
val expectParamsType = List(typeOf[Array[String]],typeOf[Array[String]])
methodParamsType.length == expectParamsType.length &&
methodParamsType.zip(expectParamsType).forall{case (l,r)=>l =:= r }
})
// or
// .find(e=>e.isPrimaryConstructor)
// .find(e=>e.paramLists.head.length == 2)
.get
Я нашел подход для фильтрации по типам параметров
ключ в том, что мы можем получить типы параметров метода с помощью methodSymbol.paramLists.head.map (_. info)
val ru: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe
def m: ru.Mirror = {
ru.runtimeMirror(Thread.currentThread().getContextClassLoader)
}
//reflect method, method can't be curry
def reflectMethod[T: ru.TypeTag : ClassTag](methodName: String, allScope: Boolean, types: ru.Type*)(x: T): ru.MethodMirror = {
val instanceMirror = m.reflect(x)
val methodSymbols = if (allScope) {
val members = getTypeTag(x).tpe.member(ru.TermName(methodName))
if (members.equals(ru.NoSymbol)) {
throw new NoSuchMethodException(noSuchMethodException(methodName, allScope, types: _*)(x))
}
members
.asTerm
.alternatives
.map(_.asMethod)
} else {
val decls = getTypeTag(x).tpe.decl(ru.TermName(methodName))
if (decls.equals(ru.NoSymbol)) {
throw new NoSuchMethodException(noSuchMethodException(methodName, allScope, types: _*)(x))
}
decls
.asTerm
.alternatives
.map(_.asMethod)
}
methodSymbols.foreach(item => assert(item.paramLists.size < 2, "we don't support curry method yet"))
val methodSymbol = methodSymbols.find(item =>
if (item.paramLists.head.isEmpty) {
types.isEmpty
} else {
if (types.isEmpty) {
item.paramLists.head.isEmpty
} else {
// empty forall is true
item.paramLists.head.zip(types).forall(pair => pair._1.info =:= pair._2)
}
}).getOrElse(throw new NoSuchMethodException(noSuchMethodException(methodName, allScope, types: _*)(x)))
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror
}
private def noSuchMethodException[T: ru.TypeTag : ClassTag](methodName: String, allScope: Boolean, types: ru.Type*)(x: T): String = {
s"no such method: $methodName, allScope: $allScope type: $types in ${getRuntimeClass(x)}"
}