2

let say I have 2 classes:

class A {
  def sayHello(name: String) {
    println("Hi " + name)
  }  
}

class B {
  var methodMaps = Map[String, String => Unit]()
  def registerMethod(methodName: String, method: String => Unit) {
    methodMaps += (methodName -> method)
  }
}

Okay, normally, I will call something like:

val b = new B
val a = new A
b.registerMethod("sayHello", a.sayHello)

But now I want to put the information into a config file, for example:

<method class="A" name="sayHello" />

Now, in the code it need to be something like this:

val b = new B
val className = readFromConfig()
val methodName = readFromConfig()
val aInstance = createInstanceFromReflection(className)
b.registerMethod(methodName, ...)

The problem is I don't know how to get the a.sayHello to pass to registerMethod, I can get the MethodMirror for sayHello, but how can I pass it to the registerMethod?

Thanks.

  • Is there a specific reason you need to to this with reflection? Is it not something you could do with a dependency injection model? http://stackoverflow.com/questions/11276319/using-reader-monad-for-dependency-injection – itsbruce Oct 02 '13 at 15:07

1 Answers1

0

I guess that in your case, you need plain old reflection. From your example, in the expression b.registerMethod("sayHello", a.sayHello), the Scala compiler will lift the a.sayHello into a function. When you are using dynamic class data from a file, the compiler can't help you; meaning that you need to do the work yourself. Following your own example, we should have:

val className = readFromConfig()
val methodName = readFromConfig()
val clazz = Class.forName(className)
val method = clazz.getMethods.find(x=>x.getName == "methodName" && x.getParameterTypes().length==1)
val aInstance = clazz.newInstance()

def invoke1[T,U](obj:Any, method:Method)(param:T):U = method.invoke(obj,Seq(param.asInstanceOf[java.lang.Object]):_*).asInstanceOf[U]

Now you should be able to register such construct in your map:

registerMethod(methodName, invoke1(aInstance,method) _ )

(*) This is not tested, but should be in the right direction, we use a similar construct in some part of our system.

maasg
  • 37,100
  • 11
  • 88
  • 115
  • If I understand correctly, then with your code the method will be invoked through reflection (method.invoke), right? But I don't want to do that because the performance will be terrible. And if we want to do the same thing, I think MethodMirror is better. – user2809804 Oct 02 '13 at 09:22
  • Actually, I think I cannot get it resolved by just reflection. I think I need to use macros. I'm still working with this. – user2809804 Oct 02 '13 at 09:25
  • In reflection, the expensive part is to instrospect the class. A cached method invocation, like here, is quite ok performance-wise. – maasg Oct 02 '13 at 09:41
  • I'd be curious to see a macro-based solution. Pls post if you go that way. – maasg Oct 02 '13 at 09:42
  • The performance is not so good. I've tested (10000 calls) and on my machine it was ~20 times slower. – user2809804 Oct 02 '13 at 10:16
  • Not succeeded with macros yet. Actually, I still learning how to generate code with it. It's not easy as Java (e.g using javaassist) – user2809804 Oct 02 '13 at 10:18
  • @user2809804 I did a micro benchmark and I got results of ~2x slower with the reflection invoke, which is pretty OK in my book. Have a look: https://gist.github.com/maasg/6808879 – maasg Oct 03 '13 at 12:18