public class Star{
public static ArrayList initdata(String pattern) {
ArrayList data = new ArrayList();
if (pattern != "") {
ModelCollection mc = Star.find(pattern, 0);
Iterator dataIterator = mc.iterator();
while (dataIterator.hasNext()) {
Star star = (Star) dataIterator.next();
data.add(star.getName());
Debug.trace("StarName" + star.getName());
}
}
Collections.sort(data);
return data;
}
}
I want to invoke method initdata using reflection, I tried to write something like this , but it does not work:
Class c = Class.forName("com.cubiware.fyretv.application.model.Star");
par[0] = String.class;
Method mthd = c.getMethod("initdata", par);
ArrayList output = (ArrayList) mthd.invoke(null, null);
try
ArrayList output = (ArrayList) mthd.invoke(null, (String)null);
It's not good idea to pass null, when method expects Object...
May be this will help
Calling Java varargs method with single null argument?
First, Your check seems weird to me: try if (pattern != null) instead of if (pattern != "").
Why don't you pass the par array, you have illegal argument exception I think. try passing arguments array.
Object[] args = {"someString / maybe null"};
ArrayList output = (ArrayList) mthd.invoke(null, args);
Obviously, your invoke call is similiar to
initdata(null);
Now, inside initdata you do not filter the case where pattern == null which leads us to a call
Star.find(null, 0);
We do not know the implementation of this method - if we're lucky, we get an empty collection. Otherwise, I expect a NullPointerException either in Star.find or later at mc.iterator()
$ javac -cp dp4j-1.2-SNAPSHOT-jar-with-dependencies.jar -Averbose -All Star.java
Star.java:12:
import com.dp4j.*;
public class Star {
public Star() {
super();
}
public static ArrayList initdata(String pattern) {
return null;
}
#Reflect()
public static void main(String[] args) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.NoSuchMethodException, java.lang.reflect.InvocationTargetException, java.lang.IllegalArgumentException {
final java.lang.reflect.Method initdataWithStringMethod = Class.forName("Star").getDeclaredMethod("initdata", .java.lang.String.class);
initdataWithStringMethod.setAccessible(true);
initdataWithStringMethod.invoke("", new .java.lang.Object[1][]{null});
final java.lang.reflect.Method printlnWithStringMethod = Class.forName("java.io.PrintStream").getDeclaredMethod("println", .java.lang.String.class);
printlnWithStringMethod.setAccessible(true);
printlnWithStringMethod.invoke(System.out, new .java.lang.Object[1][]{"Varargs + reflection? No problem"});
}
}
public static void main(String args[]) {
^
$ java Star
Varargs + reflection? No problem
Related
I'm invoking some method of Class's instance using the method.invoke(instance, args...) way but for each method inside the instance, as the invoke Javadoc rightly points out, each argument must be manually specified.
Thinking about Spring... how it could valorize parameters in controller's method behind the hood during HTTP calls? (but surely it does in a completely different way I think...)
I wonder if there's any way in Java to dynamically pass parameters in reflection (or not even reflection) without specifying each of them singularly.
EDIT
The instance class declaration is something like:
public class Something {
public void doSth(String par1, String par2, Integer par3) {
//....
}
public void doSthElse(String par1, Boolean par2) {
//....
}
public void doSthElseMore(Integer par1) {
//....
}
}
How I'm invoking each method:
...
for (Method method : instance.getDeclaredMethods()) {
Object[] array = //BL: build array of values to pass to the invoke method.
//1. doSth may be new Object[] {"abc", "def", 123}
//2. doSthElse iteration may be new Object[] {"abc", false}
//3. doSthElseMore iteration may be new Object[] {123}
return method.invoke(instance, array);
}
...
As shown above, each method inside Something class (instance) have a different number of parameters.
On each iteration, the array have a different number of values to pass to the invoke.
Actually as #Boris says all I had to do to complete my job was to convert each parameters to the correct type. In this way Java managed to invoke the correct method of the Something class with the correct parameters types.
My project is a Vert.x application using Vavr and jodd but the last return statement shows how I managed to solve.
public Object invokeMethod(Object service, Method method, RoutingContext routingContext) throws Exception {
MultiMap queryParams = routingContext.queryParams();
Map<String, String> pathParams = routingContext.pathParams();
Buffer body = routingContext.getBody();
// 1. type, 2. name, 3. value
List<Tuple3<Class<?>, String, Object>> list = List.empty();
for (Parameter par : method.getParameters()) {
ParamQuery paramQuery = par.getAnnotation(ParamQuery.class);
if (paramQuery != null) {
list = list.push(new Tuple3<Class<?>, String, Object>(par.getType(), paramQuery.value(),
queryParams.get(paramQuery.value())));
}
}
// TypeConverterManager used to "covnert" each object (String) from the HTTP call to the correct data type
return method.invoke(service, list.reverse()
.map(mapper -> TypeConverterManager.lookup(mapper._1()).convert(mapper._3())).toJavaArray());
}
However, this project can be found on GitHub
Since I notice you are using an Integer instead of a int (so no primitives parameters in your examples), you can send null to all your methods without any problems.
So you can create an array of the correct length and this will work in your case.
public static Object[] getParametersArray(Parameter[] param){
Object[] array = new Object[param.length];
// create default primitive values based on param[#].getType()
return array;
}
Then, all you have to do is to iterate the method:
Labo l = new Labo();
for(Method m : Labo.class.getDeclaredMethods()){
if((m.getModifiers() & Modifier.STATIC) > 0){
System.out.println("SKIP " + m.getName());
continue;
}
try {
m.invoke(l, getParametersArray(m.getParameters()));
} catch (Exception e) {
e.printStackTrace();
}
}
Notice the skipped static method, mostly because if you run this in the method containing the main method, you will have a recursive call.
This was tested with :
public void test(String s){
System.out.println("test String " + s);
}
public void test2(String s1, String s2){
System.out.println("test String " + s1 + " | String " + s2);
}
public void test(Integer s){
System.out.println("test Integer " + s);
}
SKIP main
test String null
test Integer null
SKIP getParametersArray
test String null | String null
Note : If you need to manage some primitive values, you will need to get the type of the parameter to provide a default value instead of null
I am taking in an array of methods and I want to chain them together to modify an object that I am working in.
For example I start with
"getStuff().get(1).get(3).setMoreStuff().put(stuff,6)"
I split it into an array called methods, and clean up the parameters inside each method and I try to modify this.
Object res = this;
String[] methods = targetString.split("\\.(?=\\D)");
for (String m : methods){
List<Object> params = new ArrayList<Object>();
List<Object> params = new ArrayList<Object>();
for (String p : m.split("\\(|,|\\)")) {
try {
if (p.indexOf(".") != -1){
double tempD = Double.parseDouble(p);
params.add(tempD);
} else {
int tempP = Integer.parseInt(p);
params.add(tempP);
}
} catch (Exception ex) { //not a number
params.add(p);
}
}
switch (params.size()) {
case 1:
res = res.getClass().getMethod(
params.get(0)
).invoke(res);
break;
case 2:
res = res.getClass().getMethod(
params.get(0),
params.get(1).getClass()
).invoke(res, params.get(1));
break;
case 3:
res = res.getClass().getMethod(
params.get(0),
params.get(1).getClass(),
params.get(2).getClass()
).invoke(res, params.get(1), params.get(2));
break;
}
in the end I notice that res has been modified the way that I expect. All the getters and setters are called correctly. But of course the underlying object "this" refers to has not been changed!
I guess I'm just calling the getters and setters of the copy I made in the first line!
now I can't just use
this.getClass().getMethod(...).invoke(...)
because I need to call the same getMethod on the object returned by this call.
To clarify:
Object res = this;
creates a "pointer" to this. So that when I call
res.getStuff().setStuff(foo)
this will also be modified.
but it seem that when I call
res = res.getStuff();
res = res.setStuff();
like I do in my loop,
this does not modify the underlying object this refers to?
Edit: Included more code as per request.
Edit2: added anther example, to clarify my problem.
Edit3: tried to add more code, its a bit hard to add a working program without including every class
Your general approach should be fine (although your approach to parameter conversion is somewhat ugly) - it's the specifics that are presumably causing you problems. Here's a short but complete program demonstrating calling methods and then seeing the difference afterwards:
import java.lang.reflect.*;
class Person {
private String name = "default";
public String getName() {
return name;
}
// Obviously this would normally take a parameter
public void setName() {
name = "name has been set";
}
}
class Test {
private Person person = new Person();
public Person getPerson() {
return person;
}
// Note that we're only declaring throws Exception for convenience
// here - diagnostic code only, *not* production code!
public void callMethods(String... methodNames) throws Exception {
Object res = this;
for (String methodName : methodNames) {
Method method = res.getClass().getMethod(methodName);
res = method.invoke(res);
}
}
public static void main(String[] args) throws Exception {
Test test = new Test();
test.callMethods("getPerson", "setName");
System.out.println(test.getPerson().getName());
}
}
The output is "name has been set" just as I'd expect. So see if you can simplify your code bit by bit, removing extra dependencies etc until you've got something similarly short but complete, but which doesn't work. I suspect you'll actually find the problem as you go.
Object does not change reference, its VALUE changes. So if you will call this.get("some key"), you will get value that the same value that you put using reflection.
Right?
This is my situation and i don't know if it is possible,
I need some Ideas.
I Have My MasterTable Object populated with Data
MasterTable MT; //already with data
a list for methods
List<String> mymethods = new ArrayList<String>();
mymethods.add("getName");
mymethods.add("getLocation");
and i have an array MasterTable Methods,
Class<MasterTable> masterclass = MasterTable.class;
Method[] masterMethods = masterclass.getMethods();
What i want is to loop through MasterTable methods and when i find masterMethod matching my criteria then i print value for that method.
e.g.
for (Method mm : masterMethods) {
if(mymethods.contains(mm.getName)){
//print method matching MT.get Method Matching mm.getName
System.out.println("print MT.getMethodMatchingmm.getName()");
}
}
Is it possible to do this?
Sure!
if (mymethods.contains(mm.getName()) {
Object result = mm.invoke(MT);
// do anything with result
}
I've tried it the other way round, to avoid non-wanted matches of overloaded methods:
public static void callGetters(Object instance, String... names)
throws Exception {
for (String name : names) {
Method method = instance.getClass().getMethod(name);
System.out.println(name + ": " + method.invoke(instance));
}
}
/**
* #param args
*/
public static void main(String[] args) throws Exception {
callGetters(new MyObject(), "getName", "getLocation");
}
I am having some trouble with passing data of an array from one class to the next.
edits
I am now no longer getting the error, and my code compiles, but as I had been warned, I got null for every element of the array. Now that I have taken out the static modifiers though, it still gives me null. I have also updated the code.
Here is the class where the array is created.
public class AssignSeat {
String[] arrangement = new String[12];
public void SeatStart() {
arrangement[0] = "Collins";
arrangement[2] = "Faivre";
arrangement[3] = "Kinnard";
arrangement[6] = "Morgans";
arrangement[7] = "Rohan";
arrangement[8] = "Shatrov";
arrangement[9] = "Sword";
arrangement[11] = "Tuckness";
System.out.format("%-15s%-15s%n", "seat", "passenger");
for (int i=0; i<arrangement.length; i++) {
System.out.format("%-15s%-15s%n", i+1, arrangement[i]);
}
}
public String[] getArrangement() {
return arrangement;
}
public void setArrangement(String[] arrangement) {
this.arrangement = arrangement;
}
}
and here is the method trying to access the information. It is specifically the for loop that I need help with so Ignore other areas where there are mistakes. Thank you.
public void actionPerformed(ActionEvent event) {
Scanner scanner = new Scanner(System.in);
AssignSeat seat = new AssignSeat();
if(event.getSource() instanceof JButton){
JButton clickedButton = (JButton) event.getSource();
String buttonText = clickedButton.getText();
if (buttonText.equals("first class")) {
entername.setVisible(true);
seatnum.setVisible(true);
confirmed.setVisible(true);
inputline.setVisible(true);
outputline.setVisible(true);
if ((seat.arrangement[1] == null)) {
System.out.println(seat.arrangement[0]);
System.out.println(seat.arrangement[2]);
two.setForeground(Color.green);
}
} else if (buttonText.equals("coach")) {
//System.out.println("so does this!");
entername.setVisible(true);
seatnum.setVisible(true);
confirmed.setVisible(true);
inputline.setVisible(true);
outputline.setVisible(true);
if ((seat.arrangement[4] == null)) {
five.setForeground(Color.green);
}
if ((seat.arrangement[5] == null)) {
six.setForeground(Color.green);
}
if ((seat.arrangement[10] == null)) {
eleven.setForeground(Color.green);
}
}
}
}
The problem lies in the fact that the array was declared as static, but the initialization code for it is in the constructor. Remove all the static modifiers in the original code, and replace this part:
if (AssignSeat.getArrangement()[1].equals("null"))
With this:
AssignSeat assign = new AssignSeat();
if (assign.getArrangement()[1] == null)
Also notice that "null" is not a null value, use null (without quotes) for that.
A different approach would be to leave the array as an static member, but initialize it statically, like this:
private static String[] arrangement = new String[12];
static {
arrangement[0] = "Collins";
arrangement[2] = "Faivre";
arrangement[3] = "Kinnard";
arrangement[6] = "Morgans";
arrangement[7] = "Rohan";
arrangement[8] = "Shatrov";
arrangement[9] = "Sword";
arrangement[11] = "Tuckness";
}
In that case, this would work:
if (AssignSeat.getArrangement()[1] == null)
But I still believe that making the array static is going to be problematic if several instances of the class happen to be modifying its contents.
Replace
if (AssignSeat.getArrangement()[1].equals("null"))
with
if (AssignSeat.getArrangement()[1] == null)
If the value is null, you can't invoke methods (like equals) on it. You need to compare the value directly to null, which is a constant rather than a string.
Ok, I'm a bit confused as to what you're trying to do in the first class. You are initializing a static array from an instance method...
In other words, the String values in the array will be null until you call SeatStart from an instance of the class.
Try to initialize the String array from the static constructor for AssignSeat to make sure it has been initialized before you use it: http://www.snippetit.com/2009/05/java-static-variables-static-methods-and-static-constructor/
You are trying to use an attribute of a class, without instantiating the object first. Until you call a default/user-defined constructor, there is no memory dedicated to the attribute of that object.
Even though you manage to call the method you are using a static method, which can be called without an instance of the object being required.
Create a constructor for the object (or use a default constructor) and then you will be able to access your attribute because your object will be on the heap and have memory allocated for the string[].
Simply define the SeaStart as an Array.
public String[] SeatStart() {
arrangement[0] = "Collins";
arrangement[2] = "Faivre";
arrangement[3] = "Kinnard";
return arrangement;
}
For convinience, make a new array to copy the array from AssignSeat class. Then retrieve the value from that array.
public void actionPerformed(ActionEvent event) {
AssignSeat seat = new AssignSeat();
String[] foo = seat.SeatStart();
System.out.println(foo[0]);
System.out.println(foo[1]);
System.out.println(foo[2]);
}
Though you can acces it also with:
System.out.println(seat.SeatStart()[0]);
The result would be:
Collins
null
Faivre
and that 'null' is because apparently you haven't allocate a value for arrangement[1] :-)
But in the end, it works.
I have four different classes classA, classB, classC and classD. All the four classes have the same static method search() which takes two string parameters. If i want to invoke static method search in four different classes from main class at once. How can I do that. For now my code is as follows for main class. I need to execute the same thing for other 3 classes also. How can i do that and display the results of other 3 in the same way as for classA. The way search is done in 4 classes r different but they should give the same result.
Main() {
Object[] zy;
for (String pattern : Read.arrayList) {
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern + " match is found at index : "+ results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
I strongly recommend you change this to non-static methods. Look how easy and nice is when you will seperate an interface:
public interface Common {
List<Integer> findAll(String pattern, String dataToSearch);
}
public class A implements Common ...
public class B implements Common ...
public class C implements Common ...
public class D implements Common ...
// in main:
List<Common> allYourClasses = new ArrayList<Common>();
allYourClasses.add(new A());
allYourClasses.add(new B());
allYourClasses.add(new C());
allYourClasses.add(new D());
List<Integer> result = new ArrayList<Integer>();
for (Common c : allYourClasses) {
result.addAll(c.findAll(pattern, dataToSearch));
}
1 - You should NOT do this. Avoid static methods. One of the reason being they can not be called without the exact class. A group of classes that implement a simple interfaces will work faster, safer and better in every way
2 - You can (but you shouldn't) do something like this:
for (Class<?> clazz : new Class[] { ClassA.class, ClassB.class,
ClassC.class }) {
Object[] zy = null;
String dataToSearch = "";
String[] arrayList = { "a" };
for (String pattern : arrayList) {
List<Integer> results = findAllForClass(clazz, pattern,
dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern
+ " match is found at index : " + results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
#SuppressWarnings("unchecked")
public static List<Integer> findAllForClass(Class<?> clazz, String pattern,
String dataToSearch) {
List<Integer> list = null;
try {
list = (List<Integer>) clazz.getDeclaredMethod("findAll", String.class,
String.class).invoke(null, pattern, dataToSearch);
} catch (Exception e) {
list = Collections.emptyList();
}
return list;
}
You see the #supresswarning and the try/catch? well, this is a hint: is telling you you this code is at least suspicious. It is in fact unsafe, non well performant, and is a stupid workaround.
(But we all did something like that once in our lives)
I can't really figure out why would anyone do that.
That said, you could have a method taking a Class as a parameter and calling the method explicitly by name (getMethod.../invoke()).
That puts you back in non static world and you can iterate over the classes you want to invoke. (But again, why use statics in the first place?)
Pseudo untested code:
public void invokeStatic(Class clazz, String method, Class<?> paramsTypes[], Object[] params) {
Method method = clazz.getMethod(method, paramsType);
method.invoke(params);
}
If you want to group all of the results together, just keep adding results to your list:
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
results.addAll(ClassB.findAll(pattern, dataToSearch));
// etc.