Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get reflected runtime Method from AnyRef object?

I am trying to get the reflected runtime method in an instance but it is not shown in the decls result:

val foo: AnyRef = new Object {
  def bar = 1
}
typeOf[foo.type].decls //Does not contain bar method

I tried to use Java reflection class and it works:

foo.getClass.getDeclaredMethods //It contains bar method

But I prefer to work with MethodSymbols and Scala Type than Java Class and Method reflection. How can I get the reflected MethodSymbol?


I want an method to look up an object passed as AnyRef for a method bar and call it. Something like below:

def getBarMethodFromObj(obj: AnyRef): MethodSymbol = {
  //typeOf(obj).decl(TermName("bar")) this doesn't work
}

I cannot use trait because bar can have different argument and return types and numbers. As Scala does not support varadic generic arguments, I plan to use reflection to find the method and call, but this cannot be done in Scala as well. I am currently use Java solution:

val bar = foo.getClass.getDeclaredMethods.find(_.getName == "bar")
bar.invoke(foo, params: _*)

However Java reflection does not retain generic types as it creates problem for List and Map, etc. So I want to know if I can implement this in Scala, or is there any coming solution

like image 266
SwiftMango Avatar asked Aug 02 '18 15:08

SwiftMango


1 Answers

I don't know what you're trying to do, but removing the AnyRef annotation makes your code work:

val foo = new { def bar = 1 }
typeOf[foo.type].decls // Contains bar method

If you need a type annotation (for example, in a method signature), you can use the same structural type that the compiler infers:

val foo: { def bar: Int } = new { def bar = 1 }

If you want to get the full list of methods from another method without knowing the exact type except via generics, you may be interested in TypeTag:

import scala.reflect.runtime.universe.{ TypeTag, typeTag }
val foo = new { def bar = 1 }
def getMethods[T: TypeTag](t: T) = typeTag[T].tpe.decls
getMethods(foo) // Contains bar

If you can't use TypeTag (maybe because you can't make API changes), then you're probably best off using the Java reflection API. The Scala reflection API is generally designed to use type information, so it may not work for you if you only know the type is AnyRef.


In response to your edit:

I cannot use trait because bar can have different argument and return types and numbers.

Sure you can:

trait Foo[A, B] {
  def bar(a: A): B
}

If you need multiple arguments, just have bar take a tuple instead. If you need to do some list manipulation that tuples don't support, you might consider learning about HLists and shapeless.

However Java reflection does not retain generic types...

Well, no runtime-only reflection API will help you there. There is simply no way to find out what generic parameters an AnyRef has at runtime, as that information doesn't exist on the JVM. Use TypeTags, or use a trait in the manner described above.

like image 106
Brian McCutchon Avatar answered Oct 05 '22 10:10

Brian McCutchon