I regularly use the Scala REPL for rapid Java iteration and testing, but sometimes I want to trigger some private behavior of a class, and have to recompile the code in order to make the method visible. I'd like to be able to call private Java methods directly in the REPL, without needing to make code changes.
What I've got so far:
// Calls private Java methods
// We currently define an overload for every n-argument method
// there's probably a way to do this in one method?
def callPrivate(obj: AnyRef, methodName: String) = {
val method = obj.getClass().getDeclaredMethod(methodName)
val returnType = method.getReturnType
method.setAccessible(true)
println("Call .asInstanceOf[%s] to cast" format method.getReturnType.getName)
method.getReturnType.cast(method.invoke(obj))
}
def callPrivate(obj: AnyRef, methodName: String, arg: AnyRef) = {
val method = obj.getClass().getDeclaredMethod(methodName, arg.getClass())
method.setAccessible(true)
method.invoke(obj, arg)
}
Which can be used like:
scala> callPrivate(myObj, "privateMethod", arg).asInstanceOf[ReturnedClass]
But this requires defining a near duplicate method for every n-argument method type (and requires an external cast, but I suspect that's unavoidable). Is there any way to refactor this so that one function can handle any number of arguments?
Note: I'm using Scala 2.9.1, so I'm looking for solutions using Java Reflection. Answers using Scala Reflection are welcome, but don't address my problem directly.
DISCLAIMER: There has been a while since the last time I programmed in Scala and I don't have any kind of Scala environment around to test what I am showing you. So it might have small syntax errors here and there, bear with me. Hope the rest is useful
In theory you could provide our callPrivate method with an extra variable argument that specifies the method parameters:
def callPrivate(obj: AnyRef, methodName: String, parameters:AnyRef*) = {
val parameterTypes = parameters.map(_.getClass())
val method = obj.getClass.getDeclaredMethod(methodName, parameterTypes:_*)
method.setAccessible(true)
method.invoke(obj, parameters:_*)
}
There is a flaw however. This won't work if you have a method somewhere with a signature like this:
public X someMethod(A parameter);
and A is inherited (or implemented) by class B. If you try to invoke your Scala method this way callPrivate(someObject, "someMethod", new B()) it won't work mostly because the getDeclaredMethod lookup will search for someMethod(B) instead of someMethod(A) - even when new B() is of type A too!
So that's a naive implementation. You could potentially get all the valid types of all the method parameters and perform the getDeclaredMethodlookup with all the combinations for them, however there is one more caveat in that direction: You might bump with overloaded methods that accept different combinations of the same set of parameters and you will not know which one to call (i.e. you may have someMethod(A,B) and someMethod(B,A) and you won't be able to know which one should be invoked)
One way avoid that is to force the caller to provide you with tuples instead of raw instances, each tuple has the parameter value and the parameter type to be used. So it is up to the caller to specify which method he want to invoke.
def callPrivateTyped(obj: AnyRef, methodName: String, parameters:(AnyRef,Class[_])*) = {
val parameterValues = parameters.map(_._1)
val parameterTypes = parameters.map(_._2)
val method = obj.getClass.getDeclaredMethod(methodName, parameterTypes:_*)
method.setAccessible(true)
println("Call .asInstanceOf[%s] to cast" format method.getReturnType.getName)
method.invoke(obj, parameterValues:_*)
}
// for convenience
def callPrivate(obj: AnyRef, methodName: String, parameters:AnyRef*) = {
callPrivateTyped(obj, methodName, parameters.map(c => (c, c.getClass)):_*)
}
That should do the trick.
Also, one more thing: Keep in mind that the way you are using getDeclaredMethod will only return methods (with any scope) that are implemented in obj.getClass(), meaning that it won't return any inherited method. I don't know if that is by design, if not you will need to add a recursive lookup over the superclasses of your obj.
Related
I'm new to using Kotlin, so far seems amazing, however I don't quite understand what's going on here:
class MyCallbackListener(val clickListener: (myLong: Long) -> Unit){
fun onClick(myObject: MyObjectClass) = clickListener(myObject.longField)
}
So, here's what I do understand:
I am creating a class that contains a method which receives a MyObjectClass
When I call onClick(someObject) I am actually indicating to call clickListener(someObject.longField)
I should use this callback method like this:
MyCallbackListener{ myLong ->
//Do Something with mylong, which will be myObject.longField }
Unit is a Kotlin type vaguely like void
However, I don't really understand the val clickListener: (myLong: Long) -> Unit part.
It looks like I am declaring a final field for the class which will be of type (myLong: Long) -> Unit but that does not make a lot of sense.
Why or how is this enabling me to pass in the lambda for the listener?
What's exactly going on here?
I come from a Java background so it could help if you could provide equivalent code, but that is not strictly necessary
What you're looking at is a function type.
(myLong: Long) -> Unit is the type of a function that takes a single Long parameter, and returns nothing useful. — In fact, the myLong parameter name is irrelevant here; it could be written more simply as (Long) -> Unit.
(Yes, Kotlin's Unit type is roughly equivalent to Java's void: it's what functions return if they don't have anything useful to return. That's not to be confused with Nothing, which is the return type for functions that never return at all, e.g. because they have an infinite loop or always throw an exception.)
So the caller has to provide a function for that parameter. You'd typically give a lambda, e.g.:
MyCallbackListener({ myLong -> println(myLong) })
(Note that that could be written more simply as MyCallbackListener(){ println(it) }, since if the last parameter is a lamdba it can be passed outside the brackets, and it is a keyword which can be used for the single parameter of a lambda.)
But you could instead give a function reference, e.g.:
MyCallbackListener(SomeClass::printLong)
Or an anonymous function, e.g.:
MyCallbackListener(fun(myLong: Long) { println(myLong) })
Or even an instance of an object implementing that function.
This doesn't have an exact equivalent in Java, because Java doesn't have first-class functions; it simply implements lambdas as instances of a functional interface (one with a Single Abstract Method) that the compiler infers. (First-class functions are more powerful and more general, though this example doesn't really demonstrate that.)
You understand everything correctly. In Kotlin you can pass functions/lambdas as objects and (myLong: Long) -> Unit is a declaration of a function type. Constructor of MyCallbackListener receives a function/lambda that has a single parameter of Long type and which does not return anything. Then, when you initialize an object of MyCallbackListener, you pass this lambda as shown in your example.
If you are more familiar to Java then (myLong: Long) -> Unit is something similar to Function<Long, Void> or Consumer<Long>.
This following declaration is legal in Kotlin.
fun foo(): String = "foo_1"
fun <T> foo(): T = "foo_2" as T
As bytecode we are getting:
public final static foo()Ljava/lang/String;
// signature <T:Ljava/lang/Object;>()TT;
// declaration: T foo<T>()
public final static foo()Ljava/lang/Object;
It's also possible to call both of these methods from Kotlin.
The problem comes when I'm trying to call any of them from Java:
ClassKt.foo()
Ambiguous call. Both methods match ...
How to avoid such a problem? How to deal with such methods? What if 3-rd party kt library has same issue?
The example above is a synthetic one.
Why does it work with Kotlin to begin with... In Java having two methods like:
private static String test() {
return "";
}
private static <T> T test() {
return null;
}
would result in a compile time error. And for java devs this is sort of obvious, these methods would have the same type erasure. But this is rule imposed by javac, not by the JVM where this code runs. So javac does not treat two methods as having only a different return type as overloads. Well, kotlin is a different language and since it runs on the JVM (that expects valid byte-code) it allows treating methods with only the return type being different as overloads. I am yet to look at the byte code and understand how that happens; it also seems that this will work only for generic code, so may be type erasure is slightly different in case of kotlin.
Now, things should be obvious why calling such a method from java fails. Kotlin offers a neat solution for this: #JvmName("someDistinctName"). I am not entirely sure how this works under the hood either... yet, though I assume this will create a bridge method.
EDIT
#JvmName will rename the method at the byte-code level.
You can use #JvmName to differentiate the code when it's called from java:
#JvmName("fooString")
fun foo(): String = "foo_1"
fun <T> foo(): T = "foo_2" as T
This will allow calling the String method in Java with ClassKt.fooString(), which resolves the clash.
An easy solution would be writing a helper method in Kotlin and just calling that.
Another way using only Java would be getting a MethodHandle for both methods and using them:
MethodHandle MH_fooString = lookup().findStatic(ClassKt.class, "foo", methodType(String.class));
MethodHandle MH_fooT = lookup().findStatic(ClassKt.class, "foo", methodType(Object.class));
String foo = (String) MH_fooString.invokeExact();
It's not nearly as simple and requires handling exceptions though.
I am trying to get a simple java reflection program working in Scala, and seem to be missing something ...
scala> val cl = new URLClassLoader(Array(new File("Hi.jar").toURI.toURL), getClass.getClassLoader)
cl: java.net.URLClassLoader = java.net.URLClassLoader#3c7b137a
scala> val c = cl.loadClass("Hi")
c: Class[_] = class Hi
scala> val m = c.getMethod("run")
m: java.lang.reflect.Method = public void Hi.run()
scala> m.invoke()
<console>:21: error: not enough arguments for method invoke: (x$1: Any, x$2: Object*)Object.
Unspecified value parameters x$1, x$2.
m.invoke()
^
What am I missing, as the prior line has indicated -
public void Hi.run()
What exactly is it expecting for the two arguments?
Scala is telling you exactly what your problem is: invoke needs 1+ parameters!
See the java doc:
invoke(Object obj, Object... args)
Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.
So, you have to provide at least one argument - a reference to the object (or class) you want to call that method on! As Hi.run() seems to be static, you would want to use your c as only argument to your call.
The following arguments would be the actual parameters that your "reflected" method expects. In your case, no further arguments.
Long story short: you better keep the excellent tutorials from Oracle on reflection close to your scala console while experimenting. If you try to learn "reflection" by trial&error; I guarantee you: a lot of frustrating trials with many strange errors. Really: the reflection API is not very forgiving when you don't know what you are doing; even the slightest mistakes can lead to very unexpected results.
There is nothing specific to Scala there. Method.invoke requires the at least one argument being the instance on which it's applied (or null for a static method).
In Scala, you can use structural typing for such simple case.
In Scala, I am doing some Java interop. I am creating a value of class Sample.Individual and I am calling a static Java method like Sample.Individual.newBuilder(). I am using a few classes that all have this same static method (eg: Sample.Position and Sample.Feature). I want to make a function that parameterizes over them, so something like:
def p[T](s: String): T = {
val pb = T.newBuilder() // this returns a value of type T.Builder
... do stuff with pb ...
pb.build() // this returns a value of type T
}
but this tells me "not found: value T"
What is the right way to parameterize the type Sample.Individual that lets me also call static methods contained in it from Scala?
I'd strongly recommend you don't go forward with this idea. Even if you find a way to do it, it's not idiomatic Scala. It's remotely possible you shave off a few lines of code, but you loose in readability and performance; bad trade-off.
The problem you're having is that you're giving your p function a type parameter T, and then invoking the newBuilder method on that type T. You can't do that, because Scala knows nothing about your type T.
Instead, first learn a little more about those Java types. Do all the potentially constructed classes extend a common type? If not, you're out of luck (you're technically not out of luck if you choose to pimp the library you're using, but please don't :P).
Otherwise, just import those types (including the generic one). Put the generic type as the type of your p function and match on the string, then just instantiate your type. Strings can be whatever, so you probably want to return an Option[GenericClass] rather than a GenericClass.
e.g.
import xxx.GenericClassBuilder
import xxx.GenericClass
import xxx.ClassA
import xxx.ClassB
def p(s: String): Option[GenericClass] = {
val maybePb: Option[GenericClassBuilder] = s match {
case "ClassA" => Some(ClassA.newBuilder())
case "ClassB" => Some(ClassB.newBuilder())
case _ => None
}
maybePb.foreach { pb => ... do side-effecty stuff with pb ...}
maybePb.map(_.build())
}
I have a method callMethod that takes arguments: methodName (String) and parameters(Object[]).
Now, everything seemed fine for me at the beginning, but I've stumbled upon a problem.
You have to know the type of the Object to use reflection. So far I was determining it in such way:
Class[] getParameterTypes(Object[] parameters) {
Class[] parameterTypes = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
return parameterTypes;
}
The callMethod is used to invoke method from external source. And it seems to fail when those methods have primitive parameters as types, or interfaces (List etc.) and I know why.
My question is:
Is there any way around this to keep it that/similar way, or the only solution is to pass the type information (f.e Integer.TYPE for primitives etc.) to the method mentioned above:
callMethod(String methodName, Object[] parameters, Class[] parameterTypes);
Thanks for any help.
Your code would also fail if the caller expected overload resolution based on their variable types. For example:
void foo(String x) {}
void foo(Object x) {}
Object x = "text";
foo(x);
... will call foo(Object) rather than foo(String), but your reflection code will call foo(String).
Now, we don't really know enough about your use case to say whether that's a problem. If the caller does know exactly which method they want to call, with which parameter types, then it's best if they pass them.
If the above situation just wouldn't occur within your context, you may well want to perform some sort of rudimentary overload resolution within your reflection code:
Find all methods with the right name and the right number of parameters.
Check each method to see whether all the argument values are assignable to the corresponding parameter types (see Class.isAssignableFrom - you may need to handle primitives separately).
If there are multiple matching methods, apply some rules to determine which to call. Those rules don't have to be the same as Java, but you need to make sure they're clear.