Is this possible? I am creating a an object from a JSON string with this code:
String obj = new Gson().toJson(jsonArray.getJSONObject(i));
String className = getClassName(jsonArray.getJSONObject(i));
Class targetClass = null;
try {
targetClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//Create Object
Object data = new Gson().fromJson(obj, targetClass);
I then do some database stuff, get a return key value and I want to set that key on the bean object using its setId() setter, but I don't want to have to cast the specific type of object to the generic object because that would require my repeating the exact same code many times just to cast the object.
key = contactsListDAO.manageDataObj(data, sql, true);
((PhoneNumber) data).setId(key);
Can I use some sort of if statement to check if the object contains an id property and then set the id on the generic object without having to cast?
Here is my working code. For some reason I could never find the method using class.getMethod() so I had to loop through an array of methods and match the names to the setId method that I knew existed. From there using invoke was the key to setting the property correctly.
public void setIdOnObject(Object obj, int id, Class<?> targetClass) {
Method[] methods = targetClass.getMethods();
for(Method i : methods) {
if(i.getName().equals("setId")) {
try {
i.invoke(obj, id);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
reflection can do that but I think maybe here you should do something else.
Write a utility function
public static <T> T fromJson(String json, Class<T> clzz)
{
return (T) new Gson().fromJson(obj, targetClass);
}
and then you can call it like so
PhoneNumber data = fromJson(obj, PhoneNumber.class);
no more conversion.
EDIT : if using "Object" is a constraint you can use reflection
public void setIdOnObject(Object obj, Object id)
{
try{
Method m = obj.getClass().getMethod("setId",id.getClass());
m.invoke(obj, id );
}catch(NoSuchMethodException e){ return false; } catch (InvocationTargetException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (IllegalAccessException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
Here is a working example I have, just copy-paste-run.
import java.lang.reflect.InvocationTargetException;
public class Reflection
{
public static void main( String[] args )
{
MyParent p = new MyParent();
setParentKey( p, "parentKey" );
MyObj o = new MyObj();
setParentKey( o, "myParentKey" );
setMyKey( o, "myKey" );
System.out.println( "p = " + p );
System.out.println( "o = " + o );
}
public static void invokeMethod( Object p, Object k, String methodName )
{
try
{
p.getClass().getMethod( methodName, k.getClass() ).invoke( p, k );
}
catch ( NoSuchMethodException e )
{
e.printStackTrace();
}
catch ( InvocationTargetException e )
{
e.printStackTrace();
}
catch ( IllegalAccessException e )
{
e.printStackTrace();
}
}
public static void setParentKey( Object p, Object k )
{
invokeMethod( p,k,"setParentKey" );
}
public static void setMyKey( Object p, Object k )
{
invokeMethod( p,k,"setMyKey" );
}
public static class MyParent
{
private Object parentKey;
public void setParentKey( String k )
{
parentKey = k;
}
#Override
public String toString()
{
return "MyParent{" +
"parentKey=" + parentKey +
'}';
}
}
public static class MyObj extends MyParent
{
private Object myKey;
public void setMyKey( String k )
{
myKey = k;
}
#Override
public String toString()
{
return "MyObj{" +
"myKey=" + myKey +
"} " + super.toString();
}
}
}
And the expected output is :
p = MyParent{parentKey=parentKey}
o = MyObj{myKey=myKey} MyParent{parentKey=myParentKey}
If you have (as you mention) "multiple bean types" and "they all have an id property", why don't you define a common interface for you beans with a setId method?
You'll get your beans and just cast to the interface which will be a safe and object-oriented approach. Is it a viable solution for you?
Related
Building a JUnit test class. It is being used as an autograder. Some of the submissions do not have all of the required class methods (even though it was part of specs). Autograder is of course only a part of the total grade (say 50%). It improves the issue of playing 500 games, to test whether they function as expected.
In addition to checking whether all methods exist, it would be nice to check if they are also callable.
JUnit test code snippet:
#Test
public void test_1p1t4_15() {
// Test if callable
try {
Direction d1 = new Direction();
checkMethod(d1.getClass(), "print");
} catch (Exception e) {
fail("Test fails:"+e.toString());
}
}
A checkMethod function helps show when issues are related to implementation of the method, such as visibility, e.g.
public void checkMethod( Class cls, String fnName) {
// Checks method validity for methods not including an argument
try {
Method m = cls.getMethod(fnName);
assertNotNull(m);
} catch (Exception e) {
fail("Failed: "+e.toString());
}
}
public void checkMethod( Class cls, String fnName, Class type) {
// Checks method validity for methods including an argument
try {
Method m = cls.getMethod(fnName, type);
assertNotNull(m);
} catch (Exception e) {
fail("Failed: "+e.toString());
}
}
public void testMethod( Class cls, String fnName) {
// Code here
}
public void testMethod( Class cls, String fnName, argType, argValue) {
// Code here
// Including an argument
}
This is a simple example that demonstrates how to find and invoke a method with arguments, if the method exists. You will want to call invokeIfExists in your JUnit tests. You will then be able to assert that the returned value matches whatever you expect.
import java.lang.reflect.*;
public class Main {
static Object invokeIfExists(Class<?> cls, String methodName,
Class<?>[] argTypes,
Object callingObject, Object[] args) {
try {
Method method = cls.getDeclaredMethod(methodName, argTypes);
return method.invoke(callingObject, args);
} catch (SecurityException | NoSuchMethodException e) {
System.err.println("Method " + methodName + " not found.");
} catch (IllegalAccessException | IllegalArgumentException e) {
System.err.println("Method " + methodName + " could not be invoked.");
} catch (InvocationTargetException e) {
System.err.println("Method " + methodName + " threw an exception.");
}
return null; // Or assert false, etc.
}
public static void main(String[] args) {
Direction direction = new Direction("a", "b");
// Tries to invoke "direction.print(123)"
String printResult = (String) invokeIfExists(
Direction.class, "print", new Class<?>[]{int.class},
direction, new Object[]{123});
System.out.println(printResult); // "Direction: a -> b and foo=123"
// Tries to invoke "direction.doesntExist()"
Object doesntExistResult = invokeIfExists(
Direction.class, "doesntExist", new Class<?>[]{},
direction, new Object[]{});
System.out.println(doesntExistResult); // null
}
}
class Direction {
private String from, to;
Direction(String from, String to) {
this.from = from;
this.to = to;
}
String print(int foo) {
return "Direction: " + from + " -> " + to + " and foo=" + foo;
}
}
Is there a way to do the following? Check if a class exists (in the same package) and if it does exist, check if a particular method exists, and if so, calling it?
Say that I have class X. In some method of class X, I want to do the following:
if (class Y exists) { //Maybe use Class.forName("Y")?
if ( Y has method a(String, String) ) {
call Y.a("hello", "world");
}
}
Is such a thing possible? And is doing such a thing reasonable? Thanks.
Is such a thing possible? And is doing such a thing reasonable?
Thanks.
Of course it is possible.
If you develop a program or a library that has to discover dynamically some classes, it is a very reasonable thing.
If it is not the case, it could not be.
If your need makes sense, you should ask you an additional question : should you invoke a static or instance method ?
Here is a sample example with both solutions :
ReflectionClass that contains the logic using reflection :
import java.lang.reflect.Method;
public class ReflectionCalls {
public static void main(String[] args) {
new ReflectionCalls();
}
public ReflectionCalls() {
callMethod(true);
callMethod(false);
}
private void callMethod(boolean isInstanceMethod) {
String className = "DiscoveredClass";
String staticMethodName = "methodStatic";
String instanceMethodName = "methodInstance";
Class<?>[] formalParameters = { int.class, String.class };
Object[] effectiveParameters = new Object[] { 5, "hello" };
String packageName = getClass().getPackage().getName();
try {
Class<?> clazz = Class.forName(packageName + "." + className);
if (!isInstanceMethod) {
Method method = clazz.getMethod(staticMethodName, formalParameters);
method.invoke(null, effectiveParameters);
}
else {
Method method = clazz.getMethod(instanceMethodName, formalParameters);
Object newInstance = clazz.newInstance();
method.invoke(newInstance, effectiveParameters);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
DiscoveredClass (the class we manipulate in the example)
package reflectionexp;
public class DiscoveredClass {
public static void methodStatic(int x, String string) {
System.out.println("static method with " + x + " and " + string);
}
public void methodInstance(int x, String string) {
System.out.println("instance method with " + x + " and " + string);
}
}
Output :
instance method with 5 and hello
static method with 5 and hello
Yes, this can be done. I've created a Test class in the same Package as the current class.
import java.lang.reflect.Method;
public class Sample {
public static void main(String[] args) {
Class<?> clazz = null;
try {
clazz = Class.forName("Test");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (clazz == null) {
System.out.println("class not found. Go eat some waffles and correct the name");
return;
}
Method m = null;
try {
m = clazz.getMethod("foo", null);
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (m == null) {
System.out.println("method not found. Go eat some waffles and correct the name");
return;
}
Test t;
try {
t = (Test) clazz.newInstance();
m.invoke(t, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Test {
static {
System.out.println("test...");
}
public void foo() {
System.out.println("foo");
}
}
O/P :
test...
foo
You can use Class.forName:
try {
Class yourClass = Class.forName( "classname" );
Object o = yourClass.newInstance();
} catch( ClassNotFoundException e ) {
//Throw error or whatever
}
To check if a method exists you could use the NoSuchMethodError e in a try/catch
You can do this using reflection, however it isnt really practical unless you are trying to access classes that potentially will not be present at runtime or if you are trying to access private or hidden fields. Example below.
public static void reflectionDemo(){
//Here we attempt to get the common URI class
//If it is found, we attempt to get the create method
//We then invoke the create method and print the class name of the result.
try {
Class<?> uriClass = Class.forName("java.net.URI");
//getMethod(String name, Class<?>... args);
java.lang.reflect.Method create = uriClass.getMethod("create", String.class);
//The first parameter is null because this is a static method.
//invoke(Object target, Object... args);
System.out.println(create.invoke(null, "some/uri").getClass());
//Will print class java.net.URI
} catch (ClassNotFoundException e) {
// If class doesnt exist
e.printStackTrace();
} catch (NoSuchMethodException e) {
// If method doesnt exist
e.printStackTrace();
} catch (SecurityException e) {
// See Javadoc
e.printStackTrace();
} catch (IllegalAccessException e) {
// From invoke
e.printStackTrace();
} catch (IllegalArgumentException e) {
// From invoke
e.printStackTrace();
} catch (java.lang.reflect.InvocationTargetException e) {
// From invoke
e.printStackTrace();
}
}
To find whether a class exists, you can use the forName() method on Class.
To find whether a method exists, you can use the getMethod() method on Class.
Documentation here:
https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#forName(java.lang.String)
https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getMethod(java.lang.String,%20java.lang.Class...)
For your class problem, you'd want to use code like:
try {
Class.forName("Y");
}
catch (ClassNotFoundException e) {
}
For your method problem, you'd want to use code like:
try {
Class.getMethod(a);
}
catch (NoSuchMethodException e) {
}
You can check if the Class exists with Class.forName("classname");
See this SO question: Check if class exists somewhere in package
If a method exists can be catched with NoSuchMethodError in your try/catch.
See this SO question: Check if method exists at Runtime in Java
try {
Object object = Class.forName("Y").newInstance();
object.a(String, String);
} catch( ClassNotFoundException | NoSuchMethodError ex) {
//do Something else
}
So I'm working with JSON in Java and JSON can have a base of either an Array or an Object. In my Config class, I take a class as an argument so I can create the file accordingly if it doesn't exist. I also store the class as a private field so I know in future.
However, when I get to reading the file, I'd prefer to have multiple return types though the same method name. If I return Object, I then have to cast the returned value which I want to avoid.
Current code:
public class Config {
private File dir = null;
private File file = null;
private Class clazz = null;
public Config(String program, String fileName, Class root) throws IOException {
this.dir = new File(System.getProperty("user.home") + File.separator + program);
if (!this.dir.exists()) {
this.dir.mkdir();
}
this.file = new File(this.dir + File.separator + fileName);
if (!this.file.exists()) {
this.file.createNewFile();
if (root.getName().equals(JSONArray.class.getName())) {
Files.write(this.file.toPath(), "[]".getBytes());
} else if (root.getName().equals(JSONObject.class.getName())) {
Files.write(this.file.toPath(), "{}".getBytes());
}
}
this.clazz = root;
}
public JSONArray readConfig() {
return null;
}
public JSONObject readConfig() {
return null;
}
}
Is there anyway I can do what I want without having to return Object?
multiple return types though the same method name
well, it is possible to use generic function to achieve that. For example,
public static void main(String[] args) {
try {
String t = getObject(String.class);
Integer d = getObject(Integer.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public static <T> T getObject(Class<T> returnType) throws Exception {
if(returnType == String.class) {
return (T) "test";
} else if(returnType == Integer.class) {
return (T) new Integer(0);
} else {
return (T) returnType.newInstance();
}
}
Will the following code even compile?
I'm afraid no. There are few compilation errors such as
public Object readConfig() {
try {
// Assume jsonString exists
return (this.clazz.getDeclaredConstructor(String.class).newInstance(jsonString)); <--- clazz should be getClass()
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
e.printStackTrace();
<---- missing return statement
}
}
I have a generic class which maintains an internal array(say data) and number of elements(say N) in the array (both private).I can add elements to the array ,which will update the value of N.The public API of the class doesn't have get methods for the data array or N.Still I would like to write unit tests for checking the state of array and N.
public class InternalArray<T> {
private T[] data;
private int N;
private int head;
public InternalArray() {
super();
data = (T[]) new Object[10];
N = 0;
head = 0;
}
public void add(T item){
data[head]=item;
head++;
N++;
}
public T get(){
T item = data[--head];
N--;
return item;
}
}
Here, all I can test are the public APIs .. But I need to test the internal state of the private variables . I thought I would be able to access the fields using reflection.I tried the below code, I can get the value of N. When it comes to T[] data ,I can't figure out how to convert the resulting Object to a String[] ( from call arrayf.get(inst) )
public static void demoReflect(){
try {
Class t = Class.forName("InternalArray");
System.out.println("got class="+t.getName());
InternalArray<String> inst = (InternalArray<String>) t.newInstance();
System.out.println("got instance="+inst.toString());
inst.add("A");
inst.add("B");
Field arrayf = t.getDeclaredField("data");
arrayf.setAccessible(true);
Field nf = t.getDeclaredField("N");
nf.setAccessible(true);
System.out.println("got arrayfield="+arrayf.getName());
System.out.println("got int field="+nf.getName());
int nval = nf.getInt(inst);
System.out.println("value of N="+nval);
Object exp = arrayf.get(inst);
//how to convert this to String[] to compare if this is {"A","B"}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
This gave the output below
got class=InternalArray
got instance=InternalArray#c2ea3f
got arrayfield=data
got int field=N
value of N=2
The Object that you get from the call of arrayf.get(inst) is of type Object[], not String[], because Java generics are implemented through type erasure. You can do this:
Object[] strings = (Object[])arrayf.get(inst);
System.out.println(strings.length);
for (int i = 0 ; i != strings.length ; i++) {
String s = (String)strings[i];
...
}
P.S. I am going to stay away from the discussion on whether it's a good idea to unit test the private details of the implementation.
I am trying to set a number of Enums to default value I am using the following method:
private void checkEnum(Field field, String setMethod) {
// TODO Auto-generated method stub
try {
String className = Character.toUpperCase(field.getName().charAt(0)) +
field.getName().substring(1);
Class<?> cls = Class.forName("com.citigroup.get.zcc.intf." + className);
Object[] enumArray = cls.getEnumConstants();
//set to the last Enum which is unknown
invoke(setMethod, enumArray[enumArray.length - 1] );
} catch(Exception e) {
System.out.println(e.toString());
}
}
The problem is actually setting the Enum. I have extracted the enum type but to then call the MethodInvoker. Passing in the Enum object is proving a problem. All the enums have the following as the last element of the enum array.
EnumName.UNKNOWN
However this is not being set via the invoke method which looks like:
private Object invoke(String methodName, Object newValue) {
Object value = null;
try {
methodInvoker.setTargetMethod(methodName);
if (newValue != null) {
methodInvoker.setArguments(new Object[]{newValue});
} else {
methodInvoker.setArguments(new Object[]{});
}
methodInvoker.prepare();
value = methodInvoker.invoke();
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
} catch (java.lang.reflect.InvocationTargetException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(),e);
}
return value;
}
So I'm lost as to why the
invoke(setMethod, enumArray[enumArray.length -1] );
Is not setting my Enum
I attempted to get your code running. The methodInvoker.prepare() call was throwing:
java.lang.IllegalArgumentException: Either 'targetClass' or 'targetObject' is required
So I added in the class missing parameter and the code works, if I understand your use case.
You appear to be setting a static field whose name must be the name of an Enum class under com.citigroup.get.zcc.intf with the first character in the field name downcased.
Here is my modified code:
public void checkEnum(Field field, String setMethod, Class clazz) {
try {
String className = Character.toUpperCase(field.getName().charAt(0)) +
field.getName().substring(1);
Class<?> cls = Class.forName("com.citigroup.get.zcc.intf." + className);
Object[] enumArray = cls.getEnumConstants();
//set to the last Enum which is unknown
invoke(setMethod, enumArray[enumArray.length - 1], clazz);
} catch (Exception e) {
System.out.println(e.toString());
}
}
private Object invoke(String methodName, Object newValue, Class clazz) {
Object value = null;
try {
MethodInvoker methodInvoker = new MethodInvoker(); // this was missing
methodInvoker.setTargetMethod(methodName);
methodInvoker.setTargetClass(clazz); // This was missing
if (newValue != null) {
methodInvoker.setArguments(new Object[]{newValue});
} else {
methodInvoker.setArguments(new Object[]{});
}
methodInvoker.prepare();
value = methodInvoker.invoke();
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
} catch (java.lang.reflect.InvocationTargetException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Method invocation failed. " + e.getMessage(), e);
}
return value;
}
}
My test code resembled (Show is an enum class of mine, MethodNameHelper has been previously posted to StackExchange):
public class StackExchangeTestCase {
protected static final Logger log = Logger.getLogger(StackExchangeTestCase.class);
public static Show show;
public static void setShow(Show newShow) {
show = newShow;
}
#Test
public void testJunk() throws Exception {
Method me = (new Util.MethodNameHelper(){}).getMethod();
Class<?> aClass = me.getDeclaringClass();
Field att1 = aClass.getField("show");
show = null;
methodNameHelper.checkEnum(att1, "setShow", aClass);
System.out.println(show); // worked
}
}