[This post has been edited to include a simplified copy/paste version of the underlying issue.]
I'm working on a Reflection project that will have some functionality similar to JUnit, but I'm running into an obstacle where the program seems to feel I have 2 different versions of the same class.
I've written a simple Car class as follows.
public class Car {
private String name;
public Car(String n) {
name = n;
System.out.println(name + " was constructed.");
}
public void honk() {
System.out.println("beep beep");
}
public void crash(Car other) {
System.out.println(name + " crashes into " + other.name);
}
}
I can successfully test the functionality of the car like this:
public class CarRunner {
public static void main(String[] args) {
Car a = new Car("Model T");
Car b = new Car("Tesla");
a.honk(); //prints "beep beep"
a.crash(b); //prints "Model T crashes into Tesla"
}
}
All the stuff above works fine.
Now, I want to reproduce the results of CarRuner but with some functionality testing methods I've written using reflection. Using reflection, I can request the creation of object and invoking methods with those objects. It works great until the final test, when a user-defined class is used as an argument.
import java.io.*;
import java.lang.invoke.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.net.*;
public class TesterTool {
//Where are the class files that I am testing?
private static File classPath = new File("C:\\Users\\Spatter\\Desktop\\Autograder\\SimpleCarDemo");
public static Object makeObject(String nameOfClass, Object[] arguments) {
Object retObject = null; //goal is to get an object in here of the requested class.
try {
//What type of object are we trying to construct?
URL classUrl = classPath.toURI().toURL();
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classUrl});
Class<?> c = Class.forName(nameOfClass, true, classLoader);
//What kind of arguments do we have?
Class[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentTypes[i] = arguments[i].getClass();
}
//Lets find a constructor that can accept the type of arguments we have
Constructor con = c.getConstructor(argumentTypes);
FutureTask<?> theTask = new FutureTask<Object>(new Callable<Object>()
{
public Object call() {
Object retObject = null;
try {
retObject = con.newInstance(arguments);
} catch (Exception e) { return e; }
return retObject;
}
});
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(theTask);
retObject = theTask.get(3, TimeUnit.SECONDS);
es.shutdownNow();
if (retObject instanceof Exception) throw new Exception();
} catch (Exception e) {
System.out.print("Error: Unable to construct object" + e);
}
return retObject;
}
public static Object testMethod(Object invokingObject, String methodName, Object[] arguments) {
Object retObject = null; //if the method we test returns an object, we will do the same.
try {
//What type of object are we trying to construct?
Class c = invokingObject.getClass();
//Alternate version of getting class type using ClassLoader
//Class originalc = invokingObject.getClass();
//String nameOfClass = originalc.getName();
//URL classUrl = classPath.toURI().toURL();
//URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classUrl});
//Class<?> c = Class.forName(nameOfClass, true, classLoader);
//What kind of arguments do we have?
Class[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentTypes[i] = arguments[i].getClass();
}
//Lets find a method that can accept the type of arguments we have
Method m = c.getMethod(methodName, argumentTypes);
FutureTask<?> theTask = new FutureTask<Object>(new Callable<Object>()
{
public Object call() {
Object retObject = null;
try {
retObject = m.invoke(invokingObject, arguments);
} catch (Exception e) { return e; }
return retObject;
}
});
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(theTask);
retObject = theTask.get(3, TimeUnit.SECONDS);
es.shutdownNow();
if (retObject instanceof Exception) throw new Exception();
} catch (Exception e) {
System.out.print("Error: Unable to run method " + e);
}
return retObject;
}
public static void main(String[] args) {
//Find the Car class and invoke the constructor that receives a String parameter.
Object o1 = makeObject("Car", new Object[]{"Model T"}); //this works fine.
Object o2 = makeObject("Car", new Object[]{"Tesla"}); //this works fine.
//Invoke the honk method of object o1. No parameters required.
//The result is that "beep beep" is printed.
testMethod(o1, "honk", new Object[] {}); //this works fine.
//Invoke the crash(Car c) method of o1 using o2 as the parameter.
//This should print "Model T crashes into Tesla".
testMethod(o1, "crash", new Object[] {o2}); //this doesn't work.
}
}
This last test is where my problem is coming into play. testMethod appears to be unable to find a version of the crash method that matches my request. The crash method is supposed to receive a Car object, which it does, but it doesn't seem to be good enough.
I've also tried a very complex alternate version of this where I get all the methods of the Car class and try to find one that matches the signature, but it seems to feel that an object of Class car isn't an object of Class car. (See below.)
Class objectClass = o2.getClass();
Class[] paramTypes = method.getParameterTypes(); //where method is the Method object for crash
Class paramClass = paramTypes[0]; //there was only 1 paramType. I confirmed that it's the Car class.
System.out.println(objectClass); //prints class Car
System.out.println(paramClass); //prints class Car
if (paramClass.isAssignableFrom(objectClass)) { //always returns false?
System.out.println("I want to run this method because the signature matches.");
// o1 should invoke method using FutureTask
}
isAssignableFrom() always returns false, even though they are both Car classes. Any idea what might be the problem? I've inspected the both of the Class objects (objectClass and paramClass) and they appear to be identical, even down to the paths in the ClassLoaders.
Instead of isAssignableFrom(), I've also tried isInstance, but it didn't work either:
if (paramClass.isInstance(o2)) { //also always returns false
The problem was coming from creating a new URLClassLoader object each time I was locating a Class object. By having only 1 URLClassLoader as a static variable the issue is resolved.
Related
I pass an Object Type variable inside a method and i want to cast it to a specific class according to a flag (I am also passing the flag).
If I put the code inside the if - else statement it works, but i end up with duplicate code.
This is an existing project and I cannot mess with the objects.
public void insertReport(Object request , String requestJson , int reportFlag){
Object reportRequest;
if (reportFlag == 0 ) {
reportRequest = (MonthlyCls) request;
}else{
reportRequest = (DailyCls) request;
}
RepEntity repEntity = new RepEntity ();
repEntity.setId(reportRequest.getReportInfo().getId());
repEntity.setDate(newTimestamp(reportRequest.getReportInfo().getDate()));
Is there a way to make java "understands" the casting in compile time?
Thank you very much in advance.
Simply use an interface or an abstract class to have a common type.
public abstract class Request{
public abstract ReportingInfo getReportInfo();
}
Then Extends the class in both classes, the methods should already be implemented.
public class DailyCls extends Request {
public ReportingInfo(){ ... }
}
public class MonthlyCls extends Request {
public ReportingInfo(){ ... }
}
You can also implement the method in Request if it is possible/necessary.
That way, you just have to change the signature to accept a Request
public void insertReport(Request request, String requestJson){
RepEntity repEntity = new RepEntity ();
repEntity.setId(request.getReportInfo().getId());
repEntity.setDate(newTimestamp(request.getReportInfo().getDate()));
}
First thing first, even if you cast the object to either MonthlyCls or DailyCls, the variable reportRequest is of type Object, so casting that object will not do anything. In order to be able to "access" the specific methods of both classes, you need to write something like this:
public void insertReport(Object request , String requestJson , int reportFlag){
MonthlyCls reportRequestMonthly = null;
DailyCls reportRequestDaily = null;
if (reportFlag == 0 ) {
reportRequestMonthly = (MonthlyCls) request;
}else{
reportRequestDaily = (DailyCls) request;
}
RepEntity repEntity = new RepEntity ();
if (reportRequestMonthly != null){
repEntity.setId(reportRequestMonthly .getReportInfo().getId());
repEntity.setDate(new Timestamp(reportRequestMonthly .getReportInfo().getDate()));
} else {
repEntity.setId(reportRequestDaily .getReportInfo().getId());
repEntity.setDate(new Timestamp(reportRequestDaily .getReportInfo().getDate()));
}
EDIT: I am assuming that both the objects are not related in any way by a SuperClass or anything, I suggest you do so and you also check with instanceof if the object given is correct.
If you can update the classes existing, a solution would be to implement an adapter that would use the reflection to call the methods.
Something like this would be quite safe to use
class RequestAdapter{
private Object request;
public RequestAdapter(Object request){
if(request == null) throw new IllegalArgumentException("The request can't be null");
if(!isSupported(request)) throw new IllegalArgumentException("Type not supported : " + request.getClass().getName());
this.request = request;
}
// call the method "getReportingInfo" by reflection on the object
public ReportingInfo getReportingInfo(){
try {
return (ReportingInfo) request.getClass().getMethod("getReportingInfo").invoke(request);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException
| SecurityException e) {
e.printStackTrace();
return null;
}
}
static Class<?>[] supportedType = {
DailyCls.class,
MonthlyCls.class
};
//Check if the type is supported, to prevent any mistake with the reflection call later.
private boolean isSupported(Object request){
for(Class<?> c : supportedType){
if(c == request.getClass()){
return true;
}
}
return false;
}
}
This is really not a clean solution, but if the method public ReportingInfo getReportingInfo() is defined in every class you add in the supportedType array. This would be quite safe.
Tested with :
public void insertReport(Object request){
RepEntity repEntity = new RepEntity ();
RequestAdapter adapter = new RequestAdapter(request);
repEntity.setId(adapter.getReportInfo().getId());
repEntity.setDate(newTimestamp(adapter.getReportInfo().getDate()));
}
If anything else than a MonthlyCls or DailyCls is pass to the method, you will get an IllegalArgumentException :
Like this : new RequestAdapter("");
Exception in thread "main" java.lang.IllegalArgumentException: Type not supported : java.lang.String
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.
I'm just wondering if it's possible to invoke a method by reflection inside a thread. My idea is to pass to the thread constructor a method name so that is how I would like to specify what the thread should do (which method should be run). The code below is not working - I obtain an error "ClassNotFoundException".
public class Listener extends Thread {
/** Constructor */
private static Window win = new Window();
private static Class c;
private String parameter;
public Listener(String param) {
this.parameter = param;
}
public void run() {
try {
Class c = Class.forName("Listener");
Class partypes[] = new Class[1];
partypes[0] = String.class;
Method meth = c.getMethod("waitForWindowAppear", partypes);
Listener methobj = new Listener(parameter);
Object arglist[] = new Object[1];
arglist[0] = parameter;
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer) retobj;
System.out.println(retval.intValue());
} catch (Exception e) {
e.printStackTrace();
}
}
/** Method waits as a thread for window at class or title */
private static void waitForWindowAppear(String title) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException exc) {
System.out.println("Thread waitForWindowAppear has been stoped!");
return;
}
if (win.isWindowVisible(title)) {
// System.out.println("Window found!");
return;
} else {
// System.out.println("Waiting for window!");
}
}
}
}
Do anyone have some ideas how to solve this problem?
I would say you don't have this class Listener on your Default package, therefore, what you should do is use the fully qualified name, for example:
Class c = Class.forName("foo.bar.Listener");
The ClassNotFoundException is thrown because it doesn't find Listener class, and one reason would be the incomplete name.
Additionally, you use getMethod, this will not work with private methods, please use getDeclaredMethod instead.
From documentation:
getMethod - Returns a Method object that reflects the specified public
member method of the class or interface represented by this Class
object.
getDeclaredMethod - Returns a Method object that reflects the
specified declared method of the class or interface represented by
this Class object.
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
I would like to use Class.newInstance() but the class I am instantiating does not have a nullary constructor. Therefore I need to be able to pass in constructor arguments. Is there a way to do this?
MyClass.class.getDeclaredConstructor(String.class).newInstance("HERESMYARG");
or
obj.getClass().getDeclaredConstructor(String.class).newInstance("HERESMYARG");
myObject.getClass().getDeclaredConstructors(types list).newInstance(args list);
Edit: according to the comments seems like pointing class and method names is not enough for some users. For more info take a look at the documentation for getting constuctor and invoking it.
Assuming you have the following constructor
class MyClass {
public MyClass(Long l, String s, int i) {
}
}
You will need to show you intend to use this constructor like so:
Class classToLoad = MyClass.class;
Class[] cArg = new Class[3]; //Our constructor has 3 arguments
cArg[0] = Long.class; //First argument is of *object* type Long
cArg[1] = String.class; //Second argument is of *object* type String
cArg[2] = int.class; //Third argument is of *primitive* type int
Long l = new Long(88);
String s = "text";
int i = 5;
classToLoad.getDeclaredConstructor(cArg).newInstance(l, s, i);
Do not use Class.newInstance(); see this thread: Why is Class.newInstance() evil?
Like other answers say, use Constructor.newInstance() instead.
You can get other constructors with getConstructor(...).
Follow below steps to call parameterized consturctor.
Get Constructor with parameter types by passing types in Class[]
for getDeclaredConstructor method of Class
Create constructor instance by passing values in Object[] for
newInstance method of Constructor
Example code:
import java.lang.reflect.*;
class NewInstanceWithReflection{
public NewInstanceWithReflection(){
System.out.println("Default constructor");
}
public NewInstanceWithReflection( String a){
System.out.println("Constructor :String => "+a);
}
public static void main(String args[]) throws Exception {
NewInstanceWithReflection object = (NewInstanceWithReflection)Class.forName("NewInstanceWithReflection").newInstance();
Constructor constructor = NewInstanceWithReflection.class.getDeclaredConstructor( new Class[] {String.class});
NewInstanceWithReflection object1 = (NewInstanceWithReflection)constructor.newInstance(new Object[]{"StackOverFlow"});
}
}
output:
java NewInstanceWithReflection
Default constructor
Constructor :String => StackOverFlow
You can use the getDeclaredConstructor method of Class. It expects an array of classes. Here is a tested and working example:
public static JFrame createJFrame(Class c, String name, Component parentComponent)
{
try
{
JFrame frame = (JFrame)c.getDeclaredConstructor(new Class[] {String.class}).newInstance("name");
if (parentComponent != null)
{
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
else
{
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
frame.setLocationRelativeTo(parentComponent);
frame.pack();
frame.setVisible(true);
}
catch (InstantiationException instantiationException)
{
ExceptionHandler.handleException(instantiationException, parentComponent, Language.messages.get(Language.InstantiationExceptionKey), c.getName());
}
catch(NoSuchMethodException noSuchMethodException)
{
//ExceptionHandler.handleException(noSuchMethodException, parentComponent, Language.NoSuchMethodExceptionKey, "NamedConstructor");
ExceptionHandler.handleException(noSuchMethodException, parentComponent, Language.messages.get(Language.NoSuchMethodExceptionKey), "(Constructor or a JFrame method)");
}
catch (IllegalAccessException illegalAccessException)
{
ExceptionHandler.handleException(illegalAccessException, parentComponent, Language.messages.get(Language.IllegalAccessExceptionKey));
}
catch (InvocationTargetException invocationTargetException)
{
ExceptionHandler.handleException(invocationTargetException, parentComponent, Language.messages.get(Language.InvocationTargetExceptionKey));
}
finally
{
return null;
}
}
I think this is exactly what you want
http://da2i.univ-lille1.fr/doc/tutorial-java/reflect/object/arg.html
Although it seems a dead thread, someone might find it useful
This is how I created an instance of Class clazz using a dynamic constructor args list.
final Constructor constructor = clazz.getConstructors()[0];
final int constructorArgsCount = constructor.getParameterCount();
if (constructorArgsCount > 0) {
final Object[] constructorArgs = new Object[constructorArgsCount];
int i = 0;
for (Class parameterClass : constructor.getParameterTypes()) {
Object dummyParameterValue = getDummyValue(Class.forName(parameterClass.getTypeName()), null);
constructorArgs[i++] = dummyParameterValue;
}
instance = constructor.newInstance(constructorArgs);
} else {
instance = clazz.newInstance();
}
This is what getDummyValue() method looks like,
private static Object getDummyValue(final Class clazz, final Field field) throws Exception {
if (int.class.equals(clazz) || Integer.class.equals(clazz)) {
return DUMMY_INT;
} else if (String.class.equals(clazz)) {
return DUMMY_STRING;
} else if (boolean.class.equals(clazz) || Boolean.class.equals(clazz)) {
return DUMMY_BOOL;
} else if (List.class.equals(clazz)) {
Class fieldClassGeneric = Class.forName(((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].getTypeName());
return List.of(getDummyValue(fieldClassGeneric, null));
} else if (USER_DEFINED_CLASSES.contains(clazz.getSimpleName())) {
return createClassInstance(clazz);
} else {
throw new Exception("Dummy value for class type not defined - " + clazz.getName();
}
}