Programmatically retrive parameters and values of a method - java

it's been quite a few months that i quite Java in favor of Python. Now i'm go back to java to project constraints.
now, i'm wondering if there's a way to get all the aprameters (with values) of a functions programmatically inside the function itself.
something like this
public void foo(String arg1,String arg2, Integer arg3){
... pars = ...getpars();
}
foo("abc","dfg",123);
where getpars() should return an HashMap with name,value pairs.
so from the example should be
arg1,"abc"
arg2,"dfg"
arg3,123
is there anything like this?

Unfortunately this is impossible. The only thing you can do is to retrieve the list of parameters types of a particular method using reflection.
But there is no way to get a map with name -> value of each argument passed into the method itself.

You can't get the name of the parameter, because it's no value just a name. If you wanna have the name of the parameter in your Map define a String which matches your parameter name and put it in.
Read this similar question. The accepted answer seems to have a solution for this using a third party library.

You can't get the names of the parameters dynamically, nor can you find the values in any way other than using the variable names. However, JAVA has the next best thing: variable arguments. If you want to have a dynamic number of arguments, you can declare your method as follows:
public void foo(Object... args)
When you call the method, you will call it with any number of arguments; foo(1, 2, "ABC") and foo(new File("config.dat"), new Scanner(), 88.5D) are both valid calls. Inside the function, args will be an array containing all of the parameters in order.
Just a few usage tips, though. The method declaration above is, in general, not considered good form. Usually, you can be much more specific. Think hard about whether or not you need all this flexibility, and consider using a few overloaded methods or possibly passing a HashMap to the function instead. Very rarely will you actually need to have dynamic parameters in that broad of a sense.

You could use:
void foo(String... args) {
for (String arg: args) { }
for (int i = 0; i < args.length - 1; i += 2) {
map.put(args[i], args[i + 1];
}
}
foo("a", "1", "b", "2");
Or use a Map builder, see builder-for-hashmap.

There are some hacky ways of getting the parameters values of an invoked method (But you have to understand that the parameters are unnamed, the best you can do is to get arg0.... argN).
Use Proxies
Aspect oriented programming (AspectJ, Spring AOP)
Let's consider the 1st approach. Say we want to log parameters before executing the method of some interface MethodParamsInterface, here you go. If you want to use these arguments in your logic - consider to implement them in InvocationHandler (or use EasyMock instead)
interface MethodParamsInterface {
void simpleMethod(int parm1, int parm2);
void simpleMethod(int parm1, int parm2, int param3);
}
public class MethodParams implements MethodParamsInterface {
public void simpleMethod(int parm1, int parm2) {
//business logic to be put there
}
public void simpleMethod(int parm1, int parm2, int param3) {
//business logic to be put there
}
public MethodParamsInterface wrappedInstance() throws Exception {
Class<?> proxyClass = Proxy.getProxyClass(MethodParams.class.getClassLoader(), MethodParamsInterface.class);
InvocationHandler invocationHandler = new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Map<String, Object> params = new LinkedHashMap<String, Object>(args.length);
for (int i = 0; i < args.length; i++)
params.put("arg" + i, args[i]);
//printing out the parameters:
for (Map.Entry<String, Object> paramValue : params.entrySet()) {
System.out.println(paramValue.getKey() + " : " + paramValue.getValue());
}
return MethodParams.this.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(MethodParams.this, args);
}
};
return (MethodParamsInterface) proxyClass.getConstructor(new Class[]{InvocationHandler.class}).newInstance(invocationHandler);
}
public static void main(String[] args) throws Exception {
MethodParams instance = new MethodParams();
MethodParamsInterface wrapped = instance.wrappedInstance();
System.out.println("First method call: ");
wrapped.simpleMethod(10, 20);
System.out.println("Another method call: ");
wrapped.simpleMethod(10, 20, 30);
}
}

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Test01 {
public int method01(int i, int j) {
System.out.println("Original method01");
return i + j;
}
}
class ParameterWriter {
public static <T> T getObject(T inp) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(inp.getClass());
factory.setFilter(
new MethodFilter() {
#Override
public boolean isHandled(Method method) {
return true;
}
}
);
MethodHandler handler = new MethodHandler() {
#Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
for (int i = 0; i < args.length; i++) {
System.out.println(proceed.getParameters()[i].getName() + ":" + args[i]);
}
return proceed.invoke(self, args);
}
};
return (T) factory.create(new Class<?>[0], new Object[0], handler);
}
}
public class Main {
public static void main(String[] args) {
try {
Test01 test01 = ParameterWriter.getObject(new Test01());
test01.method01(2, 3);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
output:
arg0:2
arg1:3
Original method01

Related

How to extract fields value from Object in Java

Here is Demo class
public class Demo {
int i;
String s;
}
I have an instance of Demo:
Demo demo = new Demo(1, "hello");
How can I get demo field values as array of Object like:
Object[] {1, "hello"};
Updated:
For new Object[] {demo.i, demo.s} I need know fields from Demo, however I need a general way to get Object array for any class that has primitive fields and/or simple fields.
you will need to make it yourself indeed. To get it how you showed, you will want a getter method in your object
public class Demo(){
int i;
String s;
public Demo(){
//constructor
}
public Object[] getDemo{
return new Object[] {i, s};
}
}
As stated in the comments, you can do it via reflection quite easily:
public static Object[] getFieldValues(Object o) {
return Arrays.stream(o.getClass().getDeclaredFields())
.map(field -> {
try {
return field.get(o);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
})
.toArray();
}
public static void main(String[] args) {
class Demo {
int i;
String s;
}
Demo d = new Demo();
d.i = 1;
d.s = "hello";
Object[] fieldValues = getFieldValues(d);
System.out.println(Arrays.toString(fieldValues));
}
However, also as stated in the comments, this might be an "X-Y problem" and there may be better solutions to what you are actually looking to achieve.

Copy whitelist of attributes from class one instance to the other

I'd like to know if it's possible to copy a list of attributes from one instance to the other. Ideally I'd like this to be done in a typesafe way, without using attributes as strings but rather being able to generate accessor handles at build time.
For example, usage should look like:
MyJavaBean sourceBean = new MyJavaBean();
sourceBean.setX("x val");
sourceBean.setY("y val");
MyJavaBean targetBean = new MyJavaBean();
copyAttributes(sourceBean,targetBean,Arrays.asList(MyJavaBean._field_x));
And only sourceBean.x would be copied to targetBean.x
My Java is a bit rusty, I'm sure I've already seen that somewhere. I've looked at bit at Lombok but it does not seem to permit this.
Does anyone have any hint on how to achieve that?
I must say that my app will have to maintain at least 50 whitelists of this kind, for a complex migration operation, so manually building
copy methods or putting thousands of annotations on fields would be a pain, and type safety is important for me.
I'm NOT looking for:
- How to copy ALL properties from one object to another
- How to do a deep clone using Serializable
Use the Introspector / BeanInfo API:
public static <T> void copyAttributes(T source, T target, Set<String> whiteList) {
try {
Arrays.stream(
Introspector.getBeanInfo(source.getClass(), Object.class).getPropertyDescriptors())
.filter(d -> whiteList.contains(d.getName()))
.filter(d -> d.getReadMethod() != null)
.filter(d -> d.getWriteMethod() != null)
.forEach(d -> copyProperty(source, target, d));
} catch (IntrospectionException e) {
throw new IllegalStateException(e);
}
}
private static <T> void copyProperty(final T source, final T target, final PropertyDescriptor d) {
try {
Object value = d.getReadMethod().invoke(source);
d.getWriteMethod().invoke(target, value);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
I know that you could use some library like BeanUtils.
But in this specific case could you not just use plain Java libraries and create a method like this one:
static <T> void copyAttributes(T source, T target, List<Field> fieldList) throws IllegalAccessException {
for (Field f : fieldList) {
f.setAccessible(true);
f.set(target, f.get(source));
}
}
Here is a running toy example which you can execute:
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
class JavaBean {
private String X;
public String getX() {
return X;
}
public void setX(String x) {
X = x;
}
#Override
public String toString() {
return "{" +
"X='" + X + '\'' +
'}';
}
}
public class MyJavaBeanTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
JavaBean a = new JavaBean();
a.setX("x");
JavaBean b = new JavaBean();
copyAttributes(a, b, Arrays.asList(a.getClass().getDeclaredField("X")));
System.out.println(b);
}
static <T> void copyAttributes(T source, T target, List<Field> fieldList) throws IllegalAccessException {
for (Field f : fieldList) {
f.setAccessible(true);
f.set(target, f.get(source));
}
}
}
What is the problem with this simplistic approach for this use case?

Java8 dynamic proxy and default methods

Having a dynamic proxy for an interface with default methods, how do I invoke a default method? By using something like defaultmethod.invoke(this, ...) you just get your proxy invocation handler called (Which is somehow correct, cause you have no implementing class for this interface).
I have a workaround using ASM to create a class implementing the interface and delegating such calls to an instance of this class. But this is not a good solution, especially if the default method calls other interface methods (you get a delegator ping-pong). The JLS is surprisingly silent about this question...
Here a small code example:
public class Java8Proxy implements InvocationHandler {
public interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
#Test
public void invokeTest() {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class }, this);
proxy.someDefaultMethod();
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// assuming not knowing the interface before runtime (I wouldn't use a
// proxy, would I?)
// what to do here to get the line printed out?
// This is just a loop
// method.invoke(this, args);
return null;
}
}
You can use the MethodHandles type in your InvocationHandler. This code is copied from Zero Turnaround.
Constructor<MethodHandles.Lookup> constructor;
Class<?> declaringClass;
Object result;
if (method.isDefault()) {
declaringClass = method.getDeclaringClass();
constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
result = constructor.
newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).
unreflectSpecial(method, declaringClass).
bindTo(proxy).
invokeWithArguments(args);
return(result);
}
The accepted answer uses setAccessible(true) to break into MethodHandles.Lookup, something that is restricted in Java 9 and beyond. This mail describes a JDK change that works for Java 9 or later.
It is possible to get this to work on Java 8 (and later) if you can get the writer of the interface to call your utility with an instance of MethodHandles.Lookup created in the interface (so it gets the permission to access the default methods of the interface):
interface HelloGenerator {
public static HelloGenerator createProxy() {
// create MethodHandles.Lookup here to get access to the default methods
return Utils.createProxy(MethodHandles.lookup(), HelloGenerator.class);
}
abstract String name();
default void sayHello() {
System.out.println("Hello " + name());
}
}
public class Utils {
static <P> P createProxy(MethodHandles.Lookup lookup, Class<P> type) {
InvocationHandler handler = (proxy, method, args) -> {
if (method.isDefault()) {
// can use unreflectSpecial here, but only because MethodHandles.Lookup
// instance was created in the interface and passed through
return lookup
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(proxy)
.invokeWithArguments(args);
}
return ...; // your desired proxy behaviour
};
Object proxy = Proxy.newProxyInstance(
type.getClassLoader(), new Class<?>[] {type}, handler);
return type.cast(proxy);
}
}
This approach won't handle all Java 8 use cases, but it did handle mine.
Since jdk-16 this is supported in a native way, via invokeDefault.
To your example, this would be done as:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class InvocationHandlerTest {
public static void main(String[] args) {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class }, new Java8Proxy());
proxy.someDefaultMethod();
}
interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
static class Java8Proxy implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoked");
InvocationHandler.invokeDefault(proxy, method, args);
return null;
}
}
}
But you do not need an explicit implementation of the interface that you need, this can be done slightly different:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class InvocationHandlerTest {
public static void main(String[] args) {
WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
WithDefaultMethod.class.getClassLoader(),
new Class<?>[] { WithDefaultMethod.class },
(o, m, params) -> {
if (m.isDefault()) {
// if it's a default method, invoke it
return InvocationHandler.invokeDefault(o, m, params);
}
return null;
});
proxy.someDefaultMethod();
}
interface WithDefaultMethod {
void someMethod();
default void someDefaultMethod() {
System.out.println("default method invoked!");
}
}
}
I wrote up a blog entry detailing the different approaches that must be used for Java 8 and 9+: http://netomi.github.io/2020/04/17/default-methods.html
It includes code from the spring framework to handle the different cases in a clean and efficient way.
This is annoyingly stupid counter-intuitive behaviour, which I assert is a bug in method#invoke(Object,Object[]), because you can't keep things simple in an InvocationHandler, like:
if (method.isDefault())
method.invoke(proxy, args);
else
method.invoke(target, args); // to call a wrapped object
So have to do a special lookup for a MethodHandle, and bind to proxy, to call, it.
I refined the McDowell provided code as follows (simplified):
private static final Constructor<MethodHandles.Lookup> lookupConstructor;
static {
try {
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookupConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private static MethodHandle findDefaultMethodHandle(Class<?> facadeInterface, Method m) {
try {
Class<?> declaringClass = m.getDeclaringClass();
// Used mode -1 = TRUST, because Modifier.PRIVATE failed for me in Java 8.
MethodHandles.Lookup lookup = lookupConstructor.newInstance(declaringClass, -1);
try {
return lookup.findSpecial(facadeInterface, m.getName(), MethodType.methodType(m.getReturnType(), m.getParameterTypes()), declaringClass);
} catch (IllegalAccessException e) {
try {
return lookup.unreflectSpecial(m, declaringClass);
} catch (IllegalAccessException x) {
x.addSuppressed(e);
throw x;
}
}
} catch (RuntimeException e) {
throw (RuntimeException) e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static class InvocationHandlerImpl implements InvocationHandler {
private final Class<?> facadeInterface;
private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
MethodHandle mh = findDefaultMethodHandle(facadeInterface, m);
return mh.bindTo(proxy).invokeWithArguments(args);
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isDefault()) {
return invokeDefault(proxy, method, args);
}
// rest of code method calls
}
}
facadeInterface is the interface being proxied, which declares the default method, it will probably be possible to use super-interface default methods too.
Non-toy code should do this lookup before invoke is called, or at least cache the MethodHandle.

Calling a method with an argument trough reflection

I have the following code which allows me to input in the scanner the Employee getter method that I want to call and it will do it using reflection (the name of the method should not appear anywhere in the code). This works for getter methods but I now need to modify the code to do something similar for setter methods. I have been trying to figure how to do it for the past week but I have been unable. Any help would be appreciated.
Thanks.
public static void main(String[] args) {
Employee e = Employee.testEmployee(); // a sample employee
Class cls = e.getClass();
Scanner scanner = new Scanner (System.in); // to parse data the user types in
String nextCommand;
// until the user enters "quit", get the next input from the user, and if it matches
// a given command, get the desired information from the employee object
do {
System.out.print("Enter command >> ");
nextCommand = scanner.next();
Method method = null;
try{
method = cls.getMethod(nextCommand);
}
catch(NoSuchMethodException x) {
}
try{
System.out.println(method.invoke(e));
}
catch(IllegalAccessException x) {
}
catch(java.lang.reflect.InvocationTargetException x) {
}
catch(NullPointerException x) {
}
} while (! nextCommand.equals("quit"));
}
Here's a code sample that does what you want to achieve:
public class Test {
private static HashSet<Class<?>> classes = new HashSet<>();
static {
classes.add(String.class);
classes.add(Integer.class);
classes.add(GregorianCalendar.class);
}
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
X obj = new X();
obj.setField("lala");
Method method = obj.getClass().getMethod("getField", null);
System.out.println(method.invoke(obj, null));
Method setMethod = getWorkingMethod(obj);
setMethod.invoke(obj, "who let the dogs out");
System.out.println(obj.getField());
}
private static Method getWorkingMethod(Object obj) {
Method method = null;
for (Class<?> c : classes) {
try {
method = obj.getClass().getMethod("setField", c);
} catch (NoSuchMethodException | SecurityException e) {
continue;
}
if(method != null){
return method;
}
}
throw new IllegalArgumentException("No such method found!");
}
}
class X {
private String stringField;
public void setField(String s) {
stringField = s;
}
public String getField() {
return stringField;
}
}
Output:
lala
who let the dogs out
Notes:
Create a collection (I used a HashSet) that stores Class<?> objects. You will use these to iterate over the possibilities and see if a method with that argument exists.
Use a try-catch to see if the method exists (an exception is thrown when it can't find it).
This will not work for overloaded methods. If this is your scenario, you'll have to make adjustments. I expect it to be no problem though, since you said this was meant for setters (which typically don't have overloads).
You can avoid calling the getter and setter methods by directly accessing the Field through reflection.
The Field object has various get and set methods that can be used to manipulate field values.
See: http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getField%28java.lang.String%29
EXAMPLE
import java.lang.reflect.Field;
public class MyObject {
private String fieldA;
public String getFieldA() {
return fieldA;
}
public void setFieldA(String fieldA) {
this.fieldA = fieldA;
}
public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
MyObject myObject = new MyObject();
myObject.setFieldA("Test");
Class clazz = myObject.getClass();
Field field = clazz.getDeclaredField("fieldA");
field.setAccessible(true);
String fieldA = (String) field.get(myObject);
System.out.println(fieldA);
field.set(myObject, "Test2");
fieldA = (String) field.get(myObject);
System.out.println(fieldA);
field.setAccessible(false); //be sure to return field to private
}
}
Resolution (method or field resolution) in java slows down you execution time by 'orders of 10 or 100', hence not a smart design decision. So, resolve once at start time, cache method instance, and execute it from cache. Avoid frequent lookups using reflection.

How do I use getConstructor(params).newInstance(args)?

This could well be a stupid question, but I'm new to Java, so...
I've currently got some code where currently this is being used
clazz.asSubclass(asSubclassOfClass).getConstructor().newInstance()
I need to pass some arguments to the contructort so I want to change it to: clazz.asSubclass(asSubclassOfClass).getConstructor(params).newInstance(args)
What I don't understand is what I need to pass in as params and what I need to pass in as args.
Let's say I wanted to pass in a String "howdy" and some object of type XYZ called XyzObj in. How would I specify that? WHat would I pass as params and what would I pass as args?
In Java this is called Reflection.
Assuming the class has this constructor, otherwise you will get a NoSuchMethod exception I believe.
clazz.asSubclass(asSubclassOfClass)
.getConstructor(String.class,XYZ.class)
.newInstance("howdy",XyzObj);
Since you are new to Java, let me give you an easier so that you can understand what's going on under the hood when you do this.
Assume you have the following class:
public class ParentClazz{
String someVar;
public ParentClazz(){
someVar="test";
}
public ParentClazz(String someVar){
System.out.println("I have been invoked");
this.someVar=someVar;
}
}
Then you have the following main method:
public static void main(String[] args) throws ParseException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
ParentClazz.class.asSubclass(ParentClazz.class).getConstructor(String.class).newInstance("howdy");
}
If you run this you will notice the console output print message - I have been invoked. This means that using reflection you have invoked the constructor of ParentClazz.
You can do the same thing if the scenario allows you is by using standard object creation process:
ParentClazz clazz = new ParentClazz("howdy");
Hope this helps you understand it.
Here is an example of creating classes without the new keyword.
The classes take other classes both primitives and Objects as their parameters.
The example also shows the instance of a subclass and a Parent class being created
public class ConstructorInstantiateWithoutNew
{
#SuppressWarnings("rawtypes")
public static void main( String [] args )
{
Class<Drinker> clazz_drinker = Drinker.class;
Class [] paramTypes = { Fizz.class, Colour.class, int.class };
Object [] paramValues = { new Fizz(), new Colour(), new Integer(10) };
Class<Drunk> clazz_drunk = Drunk.class;
Class [] paramTypesSub = { Fizz.class, Colour.class, int.class, boolean.class };
Object [] paramValuesSub = { new Fizz(), new Colour(), new Integer(10), true };
try
{
Drinker drinker = clazz_drinker.getConstructor( paramTypes ).newInstance( paramValues );
drinker.drink();
Drunk drunk = clazz_drunk.getConstructor(paramTypesSub).newInstance(paramValuesSub);
drunk.drink();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class Drinker
{
int n;
public Drinker( Fizz f, Colour c, int n)
{
this.n = n;
}
public void drink()
{
System.out.println( "Dad drank " + (n*10) + " ml");
}
}
class Drunk extends Drinker
{
boolean trouble;
public Drunk(Fizz f, Colour c, int n, boolean inDogHouse)
{
super(f,c,n);
trouble = inDogHouse;
}
public void drink()
{
System.out.println(
"Dad is Grounded: " + trouble +
" as he drank over "+
(n*10) + " ml");
}
}
class Fizz {} class Colour {}
Hope this is useful
Kind regards
Naresh Maharaj
clazz.asSubclass(asSubclassOfClass)
.getConstructor(String.class, XYZ.class)
.newInstance("howdy", XyzObj)
Which assumes that the constructor args are in the specified order

Categories

Resources