Using getDeclaredMethod to get a private method of any super (parent) class - java

I need to call a private method via reflection. In advance I don't kow if that private method is part of the specified class or a super class. I have the name of the private method, their parameters and their parameter types.
My first approach was the following:
Object classToTest = ....;
ArrayList<Method> methods = new ArrayList<Method>();
Class<?> classIndex = classToTest.getClass();
//iterate over (super) classes to collect all methods
do{
methods.addAll(Arrays.asList(classIndex.getDeclaredMethods()));
classIndex = classIndex.getSuperclass();
}
while(classIndex.getClass()!=null);
for(Method method : methods){
//match the right method
}
//call it
method.setAccessible(true);
method.invoke(classToTest,parameterValues);
Problem with that approach: Getting the right method out of the list is not trivial as the sourcecode for .getDeclaredMethod(...) shows. Unfortunatly many private internal methods are used which thus cant't be reused...
The 2nd approach was to use the getDeclaredMethod() which does the matching for me:
Method method=null;
Class<?> classIndex = classToTest.getClass();
//iterate over (super) classes since private method may be inherited
do{
//exception catching as part of the normal code is ugly
try{
method = classIndex.getDeclaredMethod(nameOfMethod, parameterTypes);
//method found thus exit
break;
}
catch(NoSuchMethodException nsme){
//method not found thus check super class
classIndex = classIndex.getSuperclass();
}
}
while(classIndex!=null);
if(method==null) throw new NoSuchMethodException( classToTest.getClass().getName() + "." + nameOfMethod + Arrays.toString(parameterValues));
//no method matching necessary
method.setAccessible(true);
method.invoke(classToTest,parameterValues);
Disadvantage of that approach: the exception catching as part of the code flow is ugly - however I currently don't see an alternative without reimplementing the matching code of Class.java.
So does anyone see an alternative approach to get the right private method?

Related

How to use methods from another class, called by the client

I am making a program that lets the user call a class, like it takes a string input, then calls the run() method of that class, is there any way to do that? I was hoping something like:
String inp=new Scanner(System.in).nextLine();
Class cl=new Class(inp);
cl.run();
I know that the code isn't correct, but that is my main idea
Creating and using a class by name requires some preconditions:
Full name of the class must be used, including package name.
You need to know the parameters of the constructor. Usually the no-arguments constructor is used for a such use case.
(Optional) You need an interface this class must implement which declares the method "run" (or any other methods you want to use).
An example with a subclass of Runnable:
String className = "some.classname.comes.from.Client";
Class<Runnable> clazz = (Class<Runnable>) Class.forName(className);
Runnable instance = clazz.getConstructor().newInstance();
instance.run();
If you don't have a common interface, you can invoke the method using reflection:
Class<Object> clazz = (Class<Object>) Class.forName(className);
Object instance = clazz.getConstructor().newInstance();
clazz.getMethod("run").invoke(instance);
or using method with parameters:
Integer p1 = 1;
int p2 = 2;
clazz.getMethod("methodWithParams", Integer.class, Integer.TYPE).invoke(instance, p1, p2);
String Variable can't be a reference for the Class.
to to a something like changing the object depends on the input
you can use polymorphism and design patterns (Factory Pattern)

Android Reflection Method Error

I'm trying to figure out Reflection with this Android class:
Class<?> c = Class.forName("com.android.internal.widget.LockPatternUtils");
Method method = c.getDeclaredMethod("getKeyguardStoredPasswordQuality");
method.setAccessible(true);
Object object = method.invoke(c); // Error with this line
result = object.toString());
The method getKeyguardStoredPasswordQuality is declared as (no parameters):
public int getKeyguardStoredPasswordQuality() {
// codes here
}
The error I got is:
Exception: java.lang.IllegalArgumentException: expected receiver of type com.android.internal.widget.LockPatternUtils, but got java.lang.Class<com.android.internal.widget.LockPatternUtils>
How do I declare com.android.internal.widget.LockPatternUtils as a receiver?
You are passing the class to #invoke() instead of an instance of LockPatternUtils.
You can create an instance using #newInstance().
Never mind, I've figured it out. I've adapted the codes below based on this tutorial.
In case anyone is interested in the solution, here it is:
Class<?> c = Class.forName("com.android.internal.widget.LockPatternUtils");
Constructor<?>[] constructors = c.getDeclaredConstructors();
Constructor<?> constructor = null;
for (int i = 0; i < constructors.length; i++) {
constructor = constructors[i];
if (constructor.getGenericParameterTypes().length == 0)
break;
}
constructor.setAccessible(true);
Object clazz = constructor.newInstance(context, true);
Method method = clazz.getClass().getDeclaredMethod("getKeyguardStoredPasswordQuality");
Object object = method.invoke(clazz);
result = object.toString();
The above solution requires that the public constructor of LockPatternUtils.java class to be defined as:
public LockPatternUtils(Context context) {...}
If the constructor changes in the future (after 2013), the solution will need to be amended.
Note: The above is an exercise for me to understand the usage of Reflection. However, using Reflection in Android production apps should be used sparingly and when absolutely needed.

Mockito - Mocking Concrete Classes

Given the following code:
LinkedList list = mock(LinkedList.class);
doCallRealMethod().when(list).clear();
list.clear();
by executing this test, a NullPointerException is thrown from first line in LinkedList#clear:
public void clear() {
Entry<E> e = header.next;
while (e != header) {
Entry<E> next = e.next;
//Code omitted.
but header has been instantiated before:
private transient Entry<E> header = new Entry<E>(null, null, null);
Could someone please explain what's happening during mock creation?
####### UPDATE. ######
Having read all answers especially Ajay's one, I looked into Objenesis source code and find out that it's using Reflection API to create the proxy instance (through CGLIB) and therefore bypassing all constructors in the hierarchy until java.lang.Object.
Here is the sample code to simulate the issue:
public class ReflectionConstructorTest {
#Test
public void testAgain() {
try {
//java.lang.Object default constructor
Constructor javaLangObjectConstructor = Object.class
.getConstructor((Class[]) null);
Constructor mungedConstructor = ReflectionFactory
.getReflectionFactory()
.newConstructorForSerialization(CustomClient.class, javaLangObjectConstructor);
mungedConstructor.setAccessible(true);
//Creates new client instance without calling its constructor
//Thus "name" is not initialized.
Object client = mungedConstructor.newInstance((Object[]) null);
//this will print "CustomClient"
System.out.println(client.getClass());
//this will print "CustomClient: null". name is null.
System.out.println(client.toString());
} catch(Exception e) {
e.printStackTrace();
}
}
}
class CustomClient {
private String name;
CustomClient() {
System.out.println(this.getClass().getSimpleName() + " - Constructor");
this.name = "My Name";
}
#Override
public String toString() {
return this.getClass().getSimpleName() + ": " + name;
}
}
You are only asking Mockito to call the real thing on clear, the underlying object is still a fake created by Mockito for you. If you need a real LinkedList then just use the LinkedList - only the most heated purist of BDD would tell you to mock everything around you. I mean, you are not mocking Strings are you?
Mockito author himself has said that calling the real thing should be used scarcely, usually only for testing a legacy code.
If you need to spy on the real object (track the invocations) then Mockito has a feature for this too:
List list = new LinkedList();
List spy = spy(list);
With spy, you can still stub a method if you need. It basically works like a mock, but isn't ;)
Your reasoning is flawless.
The key issue is that you are not operating on the actual LinkedList object. Here is what is happening behind the scenes:
The object that you are given by Mockito's mock() is an Enhancer object from the CGLIB library.
For me it is something like java.util.LinkedList$$EnhancerByMockitoWithCGLIB$$cae81a28
which kind of acts like a Proxy, albeit with the fields set to default values. (null,0 etc)
When you mock a class the object you are using is a fake, therefore the variables are not instantiated and the methods don't work as expected. You could use reflection to set a value for the header but I really wouldn't recommend this. As theadam said, the best thing to do would be to just use a list.

How can I resolve a ParametrizedType / TypeVariable when I know the ParametrizedType of the class it's in?

I am using reflection proxies to perform additional checking on a public API. Essentially I want to wrap every object that comes back from it so that any object the caller gets their hands on is a proxy to the real object.
Java still has the whole erasure problem, so I am passing the type of the wrapped object around with it. I should know what type everything is because the entry into the API is a single, non-generic interface.
public class ProxyInvocationHandler implements InvocationHandler {
private final Object delegate;
private final Type delegateType;
public ProxyInvocationHandler(Object delegate, Type delegateType) {
this.delegate = delegate;
this.delegateType = delegateType;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) {
// Omitted: additional checks performed here.
Object result = method.invoke(delegate, args);
Type returnType = method.getGenericReturnType();
// e.g. if delegateType is List<Cat> and the method is the get method,
// returnType would be E but resultType should be Cat.
Type resultType = ???
// Utility method I will omit, it just creates another proxy instance
// using its own invocation handler.
return ProxyUtils.wrap(result, resultType);
}
}
I have looked around the Type / ParametrizedType API and can't seem to find a way to get resultType, even though delegateType and returnType should be enough information to compute this.
What is the "proper" way to do this?
You can use Java ClassMate for that purpose. You'll have to use com.fasterxml.classmate.GenericType for type tokens:
GenericType<?> delegateType = new GenericType<List<Cat>>() {};
Note the empty {} that's called the "Super-type Token" pattern.
TypeResolver typeResolver = new TypeResolver();
MemberResolver memberResolver = new MemberResolver(
ResolvedType type = typeResolver.resolve(delegateType);
ResolvedTypeWithMembers members = memberResolver.resolve(type, null, null);
ResolvedMethod[] methods = members.getMemberMethods();
Cache the results in a Map:
Map<Method, ResolvedMethod> resolved = new HashMap<>();
for (ResolvedMethod method: methods) {
resolved.put(method.getRawMember(), method);
}
Now, when you have a method declared by the delegateType, i.e. List, you can get its resolved return type:
Method method = List.class.getMethod("get", int.class);
ResolvedType resultType = resolved.get(method).getReturnType();
System.out.println("resultType = " + resultType); // prints resultType = Cat
Here's the Guava way, for people who come by in the future:
...
Type returnType = method.getGenericReturnType();
TypeToken<?> resultType = TypeToken.of(delegateType).resolveType(returnType);
I changed the type of delegateType to TypeToken<?> to make things a little easier. And of course, I added quite a bit of caching (using a LoadingCache) to get the performance down to a sensible speed. Their resolution code was slower than my initial hacked-up code which did the same thing. The difference is that I now have the confidence that it's being done correctly.

How to get a Method instance for call stack element *without* arg signature?

Disclaimer: I have 10 years' experience programming, but 8 of which is in PHP (loosely typed) -- I have been using Java now for 4 days :)
In java, I need to get the value of an annotation for a method in the call stack. As far as I can tell, I do this with the Method object. From the call stack, I have retrieved the names of the class and method (strings). This is the (abbreviated) code that I am using...
Calling Method:
public class myClass
{
#Path( "some/path/value" )
public void myMethod( String someArg ) { ... }
}
Retrieval Code:
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
String callingMethodName = trace[depth].getMethodName();
String callingClassName = trace[depth].getClassName();
Class[] signature = new Class[1];
signature[0] = String.class;
Class callingClass = Class.forName( callingClassName );
Method callingMethod = callingClass.getMethod( callingMethodName, signature );
Path annotation = callingMethod.getAnnotation( Path.class );
This works like a charm, successfully returning the value of the #Path annotation ("some/path/value")
However, if you notice, I had to supply a signature of the method I was looking for. As you can see in the class code, there is only 1 method with that name, so, theoretically, the signature of the method should be irrelevant, right? As far as I can tell from various docs/blogs/examples, I should be able to call getMethod( ) with either no 2nd argument, or with null for the 2nd argument, but if I use the following:
Class callingClass = Class.forName( callingClassName );
Method callingMethod = callingClass.getMethod( callingMethodName );
Or even:
Class callingClass = Class.forName( callingClassName );
Method callingMethod = callingClass.getMethod( callingMethodName, null );
I get a NoSuchMethodException. Am I doing something wrong here? Should I be taking a different approach altogether?
In this particular situation, the calling class/method never uses polymorphism so there is only ever 1 signature. However, said signature is not known (unless that can also be determined by data available/derivable from Thread.currentThread( )), so I need a way to get a Method object without knowing the signature.
No. The signature matters. If you don't know the exact signature, but know the name, get all the methods and iterate over them until you get a hit:
Method method = null;
for (Method m : c.getDeclaredMethods()) {
if (m.getName().equals(callingMethodName)) {
method = m;
break;
}
}
// variable "method" is the first that matched name, or null if not found
Also, since java 1.5, Class.getMethod(String, Class...) is a varargs method, so you don't need the java cruft that both the question and other answer has, ie:
This works, but avoid this crap (the old, hard way):
Class[] signature = new Class[1];
signature[0] = String.class;
Method callingMethod = callingClass.getMethod( callingMethodName, signature);
Prefer this (new way):
Method callingMethod = callingClass.getMethod( callingMethodName, String.class);
If you want to match on the method name (without worrying about the arguments), you can do something like the following:
...
private Method findMethod (Class cls, String name)
{
for (Method method : cls.getDeclaredMethods( ))
if (method.getName( ).equals(name))
return method;
}
...
Method callingMethod = findMethod (callingClass, "myMethod");
Path annotation = callingMethod.getAnnotation(Path.class);
...
Basically just a linear search over all the methods in the class of interest until you find one with the name you're after.
your code:
Method callingMethod = callingClass.getMethod( callingMethodName, null );
is looking for a method named callingMethodName which takes no arguments. There's no magic that allows getMethod to figure out that your calls should return the first or only method with that name. They retrieve the method that matches exactly what you're asking for.
otherwise, you'd have to use .getMethods() and iterate over them to find the one you want?

Categories

Resources