Java: What scenarios call for the use of reflection? - java

So from reading some of the articles, the message i got out of it was being able to modify fields and set values to classes in real time without recompiling.
so is it possible to do this to 3rd party java library created classes which no source code is available / is it possible to use reflection to modify class instances on run time?
in what other scenarios is reflection commonly used?
I am trying to understand how reflection can be applicable.

Any time you're dealing with a string at runtime and want to treat part of that string as an identifier in the language.
Remote procedure calling -- treat part of a message received over the network as a method name.
Serialization and deserialization -- convert field names to string so you can write the object's fields to a stream and later convert it back into an object.
Object-relational mappings -- maintain a relationship between fields in an object and columns in a database.
Interfaces with dynamically typed scripting languages -- turn a string value produced by a scripting language into a reference to a field or method on an object.
It can also be used to allow language features to be emulated in the language.
Consider the command line java com.example.MyClass which turns a string into a class name. This doesn't require reflection, because the java executable can turn a .class file into code, but without reflection it would not be able to write java com.example.Wrapper com.example.MyClass where Wrapper delegates to its argument as in:
class Wrapper {
public static void main(String... argv) throws Exception {
// Do some initialization or other work.
Class<?> delegate = Class.forName(argv[0]);
Method main = delegate.getMethod("main", String[].class);
main.apply(null, Arrays.asList(argv).subList(1, argv.length).toArray(argv));
}
}

One other case developing IDEs like eclipse/netbeans etc., to determine which methods in an abstract class need to be implemented by a child class, and automatically write the missing method calls for you (one example).

Injection frameworks like Guice or Spring use reflection to help you build instances of objects at runtime.

Reflection is also useful in cases where configuration is required to string things together. For example, in an application I wrote I have a #Report("debits") annotation that is simply added to methods that generate reports. Then, in the XML configuration a user can simply add:
<requiredReports="debits,blah,another"/>
This minimizes boiler plate code from mapping the XML code to the actual method, since reflection can discover the report methods and make it available directly.

I was asked to create a solution for the below statement.
"1) A diff service that:
• can calculate the differences between two objects and return the resulting
"diff"
• can apply a previously created "diff" against an original object, so that
the returned object matches the modified object that was used to calculate
the diff. "
This would have been very difficult without using reflection. Using reflection I could list all the unknown object's Class elements, properties and methods. I could use these to get the values contained in the object. I could compare the original and modified objects values, create "diff" object reflecting changes between the two objects.
Using Java reflection I could then read the instructions in the "diff" object and apply these to the original object. Java reflection gave me the tools needed to change the values on the unknown properties of the original object. I could invoke setter methods and instantiate types where needed if the original property was null to set the modified value on the original object.
The "diff" app works on any two objects of the same type, but they could be any type, both objects just have to be of the same type.
Reflection is very powerful and allow us to create true generic polymorphic methods, functions, libraries and system, where the passed object type does not need to be known at compile time. This applies when using Java Reflection and Generics together, a very powerful combination.
To end of, I have also used Java Reflection to create a generic sort function, that could sort any list of any Class Type, using any property of the Class as the sort key.As long as the calling method passed the list and the property name to use, the method would return a sorted list.

Here are some cases to use reflection in
public class Main {
public static void main(String[] args) {
displayProperties(Stage.class);
}
public static void displayProperties(Class class) {
boolean hasParam = false;
boolean hasReturn = false;
ArrayList<Method> propMethods = new ArrayList<>();
Method[] methods = clazz.getMethods();
for (Method m: methods) {
Parameter[] paraType = m.getParameters();
if(m.getParameterCount()<2) {
if ((m.getReturnType() == void.class && paraType.length == 1) || (m.getReturnType() != void.class && paraType.length == 0)) {
//Get the properties alone
propMethods.add(m);
}
}
}
for (int i = 0; i < propMethods.size(); i++) {
if (propMethods.get(i).getName().startsWith("get") || propMethods.get(i).getName().startsWith("set")) {
System.out.println(readWrite(propMethods.get(i), propMethods) + " " + propMethods.get(i).getName().substring(3)+"( "+propMethods.get(i).getReturnType().getTypeName()+" )");
} else
System.out.println(readWrite(propMethods.get(i), propMethods) + " " + propMethods.get(i).getName() + "( "+propMethods.get(i).getReturnType().getTypeName()+" )");
}
}
public static String readWrite(Method method, ArrayList<Method> propMeths) {
ArrayList<Method> temp;
temp = propMeths;
boolean readIn = false;
boolean writeIn = false;
String onlyName = method.getName().substring(3);
for (int i = 0; i < temp.size(); i++) {
//use the substring--
if (temp.get(i).getName().startsWith("get") && temp.get(i).getName().endsWith(onlyName)) {
readIn = true;
}
if (temp.get(i).getName().startsWith("set") && temp.get(i).getName().endsWith(onlyName)) {
writeIn = true;
}
}
if (readIn == true && writeIn == true)
return "rw ";
else if (readIn == true && writeIn == false)
return "r ";
else
return "w ";
}
}
Another case with the String class
public static void main(String[] args)
{
displayProperties(String.class);
}
public static void displayProperties(Class class){
clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
for(int ii = 0; ii<methods.length; ii++){
System.out.println("Method Name: "+methods[ii].getName());
System.out.println("Method Type: "+methods[ii].getReturnType());
System.out.println("Method Pa: "+methods[ii].getParameterCount());
System.out.println("Method Type: "+methods[ii].getReturnType());
}
}
Loading from XML with reflection
public static Object loadFromXml(String filePath) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
File newFile = new File(filePath);
Document doc = builder.parse(newFile);
Node root = doc.getFirstChild();
return loadObjectElement(root);
}
/**
* This method loads from an xml file and returns all the contents of the file as an object
* #param root The node passed in to the method from which the "tree" gets a new level
* #return all the contents of the xml file as an object
* #throws Exception
*/
public static Object loadObjectElement(Node root) throws Exception {
//loads the root
String studentClass = root.getAttributes().getNamedItem("class").getTextContent();
Object newStudentObject = Class.forName(studentClass).newInstance();
//gets the children nodes (may have text elements like \n)
NodeList studentFieldList = root.getChildNodes();
//iterates through the children nodes
for (int i = 0; i < studentFieldList.getLength(); i++) {
//checks to make sure the child node is not a text node
if (studentFieldList.item(i).getNodeType() != Node.TEXT_NODE) {
//checks if the current node does not have children
if (studentFieldList.item(i).getChildNodes().getLength() == 0) {
//receives data of the current node
String nameField = studentFieldList.item(i).getAttributes().getNamedItem("name").getTextContent();
String valueField = studentFieldList.item(i).getAttributes().getNamedItem("value").getTextContent();
Field declaredFieldInClass = newStudentObject.getClass().getDeclaredField(nameField);
//makes the field accessible
declaredFieldInClass.setAccessible(true);
//checks the field type
switch (declaredFieldInClass.getType().getSimpleName().toLowerCase()) {
case "integer":
case "int":
declaredFieldInClass.set(newStudentObject, Integer.valueOf(valueField));
break;
case "float":
declaredFieldInClass.set(newStudentObject, Float.valueOf(valueField));
break;
case "boolean":
declaredFieldInClass.set(newStudentObject, Boolean.valueOf(valueField));
break;
default:
declaredFieldInClass.set(newStudentObject, valueField);
}
declaredFieldInClass.setAccessible(false);
} else {
//there are children in the current node
NodeList modulesObjectList = studentFieldList.item(i).getChildNodes();
String nameField = studentFieldList.item(i).getAttributes().getNamedItem("name").getTextContent();
Field declaredFieldInClass = newStudentObject.getClass().getDeclaredField(nameField);
List<Object> modules = new ArrayList<>();
//adds the modules into the array
for (int j = 0; j < modulesObjectList.getLength(); j++) {
if (modulesObjectList.item(j).getNodeType() != Node.TEXT_NODE) {
//recursively calls the the loadObjectElement method for any sub lists
modules.add(loadObjectElement(modulesObjectList.item(j)));
}
}
//sets the modules of the specific student that the method is working with
declaredFieldInClass.set(newStudentObject, modules);
}
}
}
return newStudentObject;
}

Related

Performance penalty when using Java varargs method with no arguments

Suggest the following Java class:
/** Utility class for working with DOM nodes.
*/
public class DomNodes {
public static boolean isElement(Node pNode, String pElementName, String... pAttributes) {
if (pNode.getNodeType() == Node.ELEMENT_NODE) {
final Element e = (Element) pNode;
final String uri = e.getNamespaceURI();
if (uri == null || uri.length() == 0) {
if (pElementName.equals(e.getLocalPart())) {
if (pAttributes == null || areAttributesMatching(pNode, pAttributes)) {
return true;
}
}
}
}
}
}
Now, suggest the following two uses of that class:
isElement(node, "foo");
isElement(node, "foo", "attribute0", value0, "attribute1", value1, "attribute2", value2);
It is obvious to me, that the second invocation depends on the construction of a string array. In other words, there is a small performance penalty for the construction of that array.
The first invocation, however, isn't obvious: The compiler could implement this by passing a newly constructed array with no elements. However, it might just as well pass the value null, or a constant array.
In the former case, I could help the compiler by adding a method
isElement(Node pNode, String pElementName). Question: Would you recommend that?

java.lang.InstantiationError when creating instance of static inner class with Objenesis

I am trying to create a utility method that should be able to deep-clone any object.
(Object.clone() only works on Object implementing Cloneable and I heard it's flawed anyways.)
I am using Objenesis to create new instances of objects without the use of constructors.
However, when trying to clone a JFrame I get the following Exception:
(using this class because I think it should be a good and complex test)
java.lang.InstantiationError: [Ljava.util.concurrent.ConcurrentHashMap$Node;
at sun.reflect.GeneratedSerializationConstructorAccessor12.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
I am open to any solution, not necessarily limited to Objenesis.
My Code:
private static ObjenesisStd OBJENESIS = new ObjenesisStd();
#SuppressWarnings("unchecked")
public static <T> T clone(T object, boolean deep){
if(object == null){
return null;
}else{
try {
T clone = (T) OBJENESIS.newInstance(object.getClass());
List<Field> fields = ReflectionUtil.getAllFieldsInHierarchy(object.getClass());
for(Field field : fields){
boolean isAccessible = field.isAccessible();
boolean isFinal = ReflectionUtil.isFinal(field);
field.setAccessible(true);
ReflectionUtil.setFinal(field, false);
Class<?> type = field.getType();
if(!deep || type.isPrimitive() || type == String.class){
field.set(clone, field.get(object));
}else{
field.set(clone, clone(field.get(object), true));
}
field.setAccessible(isAccessible);
ReflectionUtil.setFinal(field, isFinal);
}
return clone;
} catch (Throwable e) {
e.printStackTrace();
//throw new RuntimeException("Failed to clone object of type " + object.getClass(), e);
return null;
}
}
}
public static void main(String[] args) {
GetterSetterAccess access = new GetterSetterAccess(JFrame.class);
JFrame frame = new JFrame("Test Frame");
for(String attr : access.getAttributes()){
System.out.println(attr + " " + access.getValue(frame, attr));
}
System.out.println("----------------------------------------------");
frame = clone(frame, true);
for(String attr : access.getAttributes()){
System.out.println(attr + " " + access.getValue(frame, attr));
}
}
EDIT: Got it to work with the accepted answer and a few more fixes:
Avoided cloning Wrappers of Primitive Types (Integer.class etc.)
Avoided cloning Classes (Objects of the class Class.class)
Stored the cloned objects in a Map and reused them, so if Object A has a reference to Object B and Object B one to Object A it doesn't get stuck in an infinite loop. I also used a Map that checks for exact equality (==) instead of using equals().
Created a custom exception class which would just be passed on instead of throwing a new exception on every level (causing a huge caused-by-depth).
I finally figured it out. Your code doesn't handle arrays. So it fails with instantiating "[Ljava.util.concurrent.ConcurrentHashMap$Node;" which is an array of Nodes.
However, I will advocate that indeed, you should not do that. You will end up with fairly complicated code. Depending on what you want to do, you could use Jackson or XStream to do a marshall / unmarshall to perform the copy.
If you really want to continue that path, you will need something like this after the null check of your clone method.
if(object.getClass().isArray()) {
int length = Array.getLength(object);
Object array = Array.newInstance(object.getClass().getComponentType(), length);
for (int i = 0; i < length; i++) {
Array.set(array, i, clone(Array.get(object, i), true));
}
return (T) array;
}

JAVA New istante of a copied object

Hello everyone I'm writing a Java program where I need to copy the values of an old object into a new one (the two must be separated, if I chance one, the other must not be affected);
Once the copy is done the data should be displayed on Jtable, however the 2 object created seems to be linked(if I try to change one the other get modified too)
I suspect the problem is this method :
public void CopiatoreDiArea(Area nuova, Area daCopiare){
nuova.setNome(daCopiare.getNome());
nuova.setInter(daCopiare.getInter());
nuova.setRischioInerente(daCopiare.getRischioInerente());
nuova.setRischioResiduo(daCopiare.getRischioResiduo());
nuova.setControlli(daCopiare.getChecklists());
nuova.setStrategicita(daCopiare.getStrategicita());
nuova.setRischiosita(daCopiare.getRischiosita());
nuova.setMediaHpReato(daCopiare.getMediaHpReato());
nuova.setProbabilitaInerente(daCopiare.getProbabilitaInerente());
nuova.setEsposta(daCopiare.isEsposta());
nuova.setStrumentale(daCopiare.isStrumentale());
nuova.setCommento(daCopiare.getCommento());
nuova.setCondivisa(daCopiare.isCondivisa());
if (daCopiare.getNomeCompleto() != null){
nuova.setNomeCompleto(daCopiare.getNomeCompleto());
}
else{
nuova.setNomeCompleto(daCopiare.getNome());
}
if (daCopiare.getInterCompany() != null){
nuova.setInterCompany(daCopiare.getInterCompany());
}
if (daCopiare.getArticoli() != null || daCopiare.getArticoli().size() != 0){
nuova.setArticoli(daCopiare.getArticoli());
}
}
If this is the wrong way, how can I accomplish that?
How do you create the new instance of nuova object?
You have to make a new instance of it, if you created the new object with the reference of the oldest they will be linked and every change in the one will be reflected in other.
If you call the method as following:
CopiatoreDiArea(new Area(), oldArea)
Your code should work as the new 'Area' has all the attributes the other object has without any references being made to the new Area.
Try this:
public Area CopiatoreDiArea(Area daCopiare){
Area nuova = new Area();
nuova.setNome(daCopiare.getNome());
nuova.setInter(daCopiare.getInter());
nuova.setRischioInerente(daCopiare.getRischioInerente());
nuova.setRischioResiduo(daCopiare.getRischioResiduo());
nuova.setControlli(daCopiare.getChecklists());
nuova.setStrategicita(daCopiare.getStrategicita());
nuova.setRischiosita(daCopiare.getRischiosita());
nuova.setMediaHpReato(daCopiare.getMediaHpReato());
nuova.setProbabilitaInerente(daCopiare.getProbabilitaInerente());
nuova.setEsposta(daCopiare.isEsposta());
nuova.setStrumentale(daCopiare.isStrumentale());
nuova.setCommento(daCopiare.getCommento());
nuova.setCondivisa(daCopiare.isCondivisa());
if (daCopiare.getNomeCompleto() != null){
nuova.setNomeCompleto(daCopiare.getNomeCompleto());
}
else{
nuova.setNomeCompleto(daCopiare.getNome());
}
if (daCopiare.getInterCompany() != null){
nuova.setInterCompany(daCopiare.getInterCompany());
}
if (daCopiare.getArticoli() != null || daCopiare.getArticoli().size() != 0){
nuova.setArticoli(daCopiare.getArticoli());
}
}
And then in your call:
Area nuova = CopiatoreDiArea(daCopiare);
If you copy primitives (or the special primitive wrapper classes) in Java, the runtime will always do a deep copy of the values. To illustrate this, I show a few examples.
See http://jdoodle.com/a/3TL for an online runnable version of the code below:
public class MyClass {
public static void main(String[] args) {
int a = 10;
int b = a;
b = 20;
System.out.println(a + " != " + b);
// We did not change a when changing b.
Integer c = 10;
Integer d = c;
d = 20;
System.out.println(c + " != " + d);
// We did not change c when we changed d, even though they are class instances.
// This shows that primitive wrappers are handled differently due to
// autoboxing.
// See https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html
MyInteger e = new MyInteger(10);
MyInteger f = e;
f.value = 20;
System.out.println(e + " == " + f);
// Changing the data contained within f also affected e. The two variables now point to the same instance.
MyInteger g = new MyInteger(10);
MyInteger h = new MyInteger(g);
h.value = 20;
System.out.println(g + " != " + h);
// We prove that we have two instances, initially containing
// the same value but changing their internal values does not
// affect each other.
}
private static class MyInteger {
public int value;
public MyInteger(int value) {
this.value = value;
}
public MyInteger(MyInteger other) {
this.value = other.value;
}
public String toString() {
return Integer.toString(this.value);
}
}
}
From your example, it seems you might be trying to copy complex classes, even lists maybe, and these will always copy a reference only when you use simple assignment.
There are a few options that you could take, for instance the copy constructor that I use above, or alternatively rely on some form of reflection or serialization that does the hard work for you.
Apache Commons Lang has a SerializationUtils class that will run through all internal data in your classes, serialize them into byte format and then try to re-instantiate new versions of the same classes with the serialized data.
The Cloner library makes use of reflection and some special performance cases for known types such as collections and calendars, with some great debugging options.
Declare a copy constructor for deep cloning your object
public class Area {
public Area() { // constructor }
public Area(Area objectToCopy) {
setNome(objectToCopy.getNome());
setInter(objectToCopy.getInter());
setRischioInerente(objectToCopy.getRischioInerente());
setRischioResiduo(objectToCopy.getRischioResiduo());
setControlli(objectToCopy.getChecklists());
setStrategicita(objectToCopy.getStrategicita());
setRischiosita(objectToCopy.getRischiosita());
setMediaHpReato(objectToCopy.getMediaHpReato());
setProbabilitaInerente(objectToCopy.getProbabilitaInerente());
setEsposta(objectToCopy.isEsposta());
setStrumentale(objectToCopy.isStrumentale());
setCommento(objectToCopy.getCommento());
setCondivisa(objectToCopy.isCondivisa());
if (objectToCopy.getNomeCompleto() != null)
setNomeCompleto(objectToCopy.getNomeCompleto());
else
setNomeCompleto(objectToCopy.getNome());
if (objectToCopy.getInterCompany() != null)
setInterCompany(objectToCopy.getInterCompany());
if (objectToCopy.getArticoli() != null || objectToCopy.getArticoli().size() != 0)
setArticoli(objectToCopy.getArticoli());
}
}
and then do it calling
Area newArea = new Area(oldArea);

Should i make this method static?

I am writing an android application that parses an xml feed about car parks.
I've created a separate class that receives an xml feed as a string, this class will then parse the string, creating objects for each car park found in the feed. I will use this each time I refresh the xml feed and need to parse it, should I make this class static or not. I am still not 100% sure on when to make something static or not.
My code:
public class XMLParser2 {
private static CarPark carPark;
private static ArrayList<CarPark> carParkListings;
// Parse XML string and save each car park object created within an Arraylist collection
public static ArrayList<CarPark> parseXML(String xml) throws XmlPullParserException, IOException {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(new StringReader(xml));
int eventType = xpp.getEventType();
carParkListings = new ArrayList<CarPark>();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("situation")) {
// Create new CarPark object to hold car park data
carPark = new CarPark();
}
// Check start tag for each element wanting to obtain and save in the
// carPark object using its associated set method
if (xpp.getName().equalsIgnoreCase("carparkstatus")) {
String status = xpp.nextText();
if (status.matches("enoughSpacesAvailable"))
carPark.setCarParkActive("Open");
else if (status.matches("carParkClosed"))
carPark.setCarParkActive("Closed");
else
carPark.setCarParkActive("Full");
}
if (xpp.getName().equalsIgnoreCase("overallstarttime")) {
carPark.setDateCreated(xpp.nextText());
}
if (xpp.getName().equalsIgnoreCase("latitude")) {
Double latitude = Double.parseDouble(xpp.nextText());
carPark.setLatitude(latitude);
}
if (xpp.getName().equalsIgnoreCase("longitude")) {
Double longitude = Double.parseDouble(xpp.nextText());
carPark.setLongtitude(longitude);
}
if (xpp.getName().equalsIgnoreCase("carparkidentity")) {
String[] identity = xpp.nextText().split("\\:");
carPark.setCarParkName(identity[0]);
carPark.setCarParkID(identity[1]);
}
if (xpp.getName().equalsIgnoreCase("occupiedspaces")) {
int occupiedSpaces = Integer.parseInt(xpp.nextText());
carPark.setOccupiedSpaces(occupiedSpaces);
}
if (xpp.getName().equalsIgnoreCase("totalcapacity")) {
int totalCapacity = Integer.parseInt(xpp.nextText());
carPark.setTotalSpaces(totalCapacity);
}
}
// Check if the tag is an end tag
else if (eventType == XmlPullParser.END_TAG) {
if (xpp.getName().equalsIgnoreCase("situation")) {
// Calculate total spaces free
carPark.setFreeSpaces();
// Add CarPark class object to carParkListings ArrayList collection
carParkListings.add(carPark);
}
}
// Move to next event
eventType = xpp.next();
}
// Return ArrayList collection
return carParkListings;
}
}
Better make it non static. You manipulate objects inside the class and if multiple classes call the same method in the same time you might have problems. For example one class calls this method and it start executing and in the same time another class calls the method and reaches the line carPark = new CarPark(); before the first one has finished. It will compromise the results.
Better use instances and make the fields carPark and carParkListings non static.
If you don't want to create any instance of XMLParser2 class when you call the parseXML method, you can make it static.
But what you should not do is use the global static property in your parseXML method.
private static CarPark carPark;
private static ArrayList<CarPark> carParkListings;
Those two variables need to define local inside the method only.
If you do not maintain any other states in XMLParser2, I think it is a good candidate for a static method since it looks like an utility method to me, but you should make the carPark and carParkListings local variables of the method itself. One should use a static method if it does not use any instance variables. You only need to specify input as parameter to the method and expect to get back some result.

java.reflection.Method.invoke(...) Not re-casting args

I am trying out Java Reflection API. I am just fetching the Method objects of any given class into a JComboBox, and on it's itemSelected, creating an interface for the parameters (and of course, a calling object.)
This works fine, no issued.
But on the invokeButton's action, I am trying to invke the selected method with given params.
Initially it said that the param count differed. I was guided by one of my friend saying that the paramVals array has references to actual values, which might be causing problem, may be due to scope. I then started creating new objects of class Object and then assigning them the values. This worked for param count. But now the problem is that the parameters are not type cast properly. Even a String typecast to Object (as it has to be an array of Objects) is not being cast back to String.
The doc says that the invoke method will cast them on it's own and if cast fails, will throw an IllegalArgumentException.
I am not getting what is causing the call of invoke method fails...
Here is the code for the frame:
package nttraining.abhay.reflectiondemo;
//imports go here
public class ReflectionFrame
extends JFrame
implements ActionListener, ItemListener{
JComboBox methods;
JButton invokeButton;
public ReflectionFrame(String title) throws HeadlessException {
super(title);
//Layout components
//adding methods of class String to a combo
Class<String> c = String.class;
Method ml[] = c.getMethods();
for(Method m : ml){
methods.addItem(m);
}
invokeButton.addActionListener(this);
methods.addItemListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(invokeButton)){
Method selected = (Method) methods.getSelectedItem();
Class paramtypes[] = selected.getParameterTypes();
Object paramVals[] = new Object[paramtypes.length];
System.out.println("Method : " + selected.toString());
for(int i=0; i<paramtypes.length; i++){
Object obj = new Object();
obj = paramtypes[i].cast(params[i].getText());
paramVals[i] = obj;
System.out.println("Added " + paramtypes[i].cast(params[i].getText()).toString() + " to params");
}
try {
result.setText(selected.invoke(object.getText(), params).toString());
} catch (Exception ex) {
System.out.println(ex.getClass().getName() + ": " + ex.getMessage());
}
}
}
#Override
public void itemStateChanged(ItemEvent e) {
Method selected = (Method) methods.getSelectedItem();
if(selected==null)
return;
Class paramtypes[] = selected.getParameterTypes();
int paramCount = paramtypes.length;
object = new JTextField();
paramNames = new JLabel[paramCount];
params = new JTextField[paramCount];
panel.removeAll();
panel.setLayout(new GridLayout(paramCount+1, 2));
panel.add(new JLabel("Calling object"));
panel.add(object);
for(int i=0; i<paramCount; i++){
paramNames[i] = (JLabel) panel.add(new JLabel(paramtypes[i].getName()));
params[i] = (JTextField) panel.add(new JTextField());
}
invalidate();
validate();
}
}
A problem I found is in this line:
obj = paramtypes[i].cast(params[i].getText());
cast does not convert objects, it only verfies that the given object is of a certain class. Since you always provide a String.class as parameter (via .getText()), this will fail for anything other then a String type parameter. Even Integer.class to primitive int will fail.
Below a piece of code that demonstrates the cast problem.
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class Q21642768 {
public static void main(String[] args) {
try {
// Calls String.indexOf(str, fromIndex) via reflection.
callStringMethod("Hello reflection world", "reflection", 1);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void callStringMethod(String s, String subString, int startIndex) throws Exception {
Class<String> c = String.class;
Method ma[] = c.getMethods();
Method indexOfSub = null;
Class<?>[] indexOfSubPTypes = null;
List<Method> stringMethods = new ArrayList<Method>();
List<Type[]> stringMethodsPTypes = new ArrayList<Type[]>();
for (Method m: ma) {
stringMethods.add(m);
System.out.print(m.getName() + ": ");
Class<?>[] mptypes = m.getParameterTypes();
stringMethodsPTypes.add(mptypes);
boolean first = true;
for (Type t : mptypes) {
if (first) {
first = false;
} else {
System.out.print(", ");
}
System.out.print(t.toString());
}
if ("indexOf".equals(m.getName())
&& mptypes.length == 2
&& mptypes[0].equals(String.class)
&& mptypes[1].equals(int.class)) {
indexOfSub = m;
indexOfSubPTypes = mptypes;
System.out.println(" <-- ");
} else {
System.out.println();
}
}
if (indexOfSub == null) {
System.out.println("target method not found");
return;
}
Object[] pValues = new Object[2];
pValues[0] = indexOfSubPTypes[0].cast(subString);
// Fails:
// pValues[1] = indexOfSubPTypes[1].cast(startIndex);
// pValues[1] = indexOfSubPTypes[1].cast(startIndex + "");
pValues[1] = startIndex;
Object result = indexOfSub.invoke(s, pValues);
System.out.println("Result: " + result);
}
}
The problem is that all your parameter values are String objects, since you get them with JTextField.getText(). String is the runtime type of these values, whereas the type of the method parameters will generally be different, and this is what matters.
To successfully invoke the method, you will first need to convert each value to the proper type specified in the paramTypes array. Neither cast() nor invoke() are going to do that for you. This means you must find a way to do the conversion from a String, basically deserializing from a String value into an object of the proper class, and that may not always be possible or too complex to do. At this point, I think you can start to imagine the complexity of what you are trying to do. This is very far from trivial. Remember that each parameter value will generally not be a simple value, but rather a full object graph, that's where the complexity is.
For example, if the type of a method parameter is an interface, how will you know which concrete implementation to instantiate? If you do find a concrete class implementing it - and that may not always be possible - how will you create instances of that class? Here you're entering a domain covered by serialization frameworks in Java. There are quite a few of these frameworks that are open source and you might want to take a look at some of them. You will find a comprenhesive list of such fraleworks here.
A few years ago, I worked on a related project, where I had to provide a Swing GUI to enable end-users to create objects of arbitrary types, used as input for a rule engine. What I came up with was a JTree with multiple roots, associated with a property sheet (i.e. a JTable with 2 columns), where the tree leaves were either simple types (primitives, primitive wrappers, Date, etc.) or object references. Each reference would point to a specific tree root representing the actual object to be later instantiated. I don't remember exactly how long it took, just that it took several months to get it tested and working.
So, I don't want to crush your hopes, but you should be aware that it's going to take a huge amount of work to do this.

Categories

Resources