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.
Related
[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.
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 a class like this
parent class DevPortalTestController is absract
public class SeleniumWebDriverFactory extends DevPortalTestController {
public static RemoteWebDriver mDriver;
public SeleniumWebDriverFactory(RemoteWebDriver whichDriver)throws UnsupportedOSException, PoisonException {
super(whichDriver);
mDriver = whichDriver;
}
public List<TestContext> getBrowserTestContext(List<String> browsers)
throws Exception {
PhoenixDriver driver = null;
List<TestContext> contexts = new ArrayList<TestContext>();
logger.info("Setting browser context...");
Login login = retrieveLoginData();
for (String browser : browsers) {
// operations
Map<String, Object> browserMap = new HashMap<String, Object>();
// Populate the map with DevPortalTestController objects.
browserMap.put(MasterConstants.BROWSER, this);
.....
.....
}
return contexts;
}
public static List<TestContext> getTestContext(List<String> browsers)
throws Exception {
SeleniumWebDriverFactory instanceSel = new SeleniumWebDriverFactory(mDriver);
List<TestContext> contexts = instanceSel.getBrowserTestContext(browsers);
return contexts;
}
}
I need to call this getTestContext method in another class
for that am doing like this.Also that class is extenting another parnet class
public class DevPortalTest extends Test {
RemoteWebDriver rmDriver ;
SeleniumWebDriverFactory selFac =new SeleniumWebDriverFactory(rmDriver);
#Override
public List<TestContext> getTestContexts() {
try {
String os = System.getProperty("os.name");
if (SystemDetail.deviceIsRunningWindows()) {
return selFac.getTestContext(ZucchiniConstants.allBrowsers);
else {
throw new TestException(os + " is not supported");
}
} catch (Exception e) {
logger.error("", e);
}
return null;
}
}
But in this place
SeleniumWebDriverFactory selFac =new SeleniumWebDriverFactory(rmDriver);
I'm getting
Default constructor cannot handle exception type PoisonException
thrown by implicit super constructor. Must define an explicit
constructor
How can i call the method getTestContext inside DevPortalTest test class?
The problem is that initializer code will be placed in the "default constructor" which cannot throw any exception. Define an empty constructor that throws the exceptions to proceed.
e.g.,
DevPortalTest() throws UnsupportedOSException, PoisonException { }
You have to add the constructor to your test code:
public DevPortalTest() throws UnsupportedOSException, PoisonException {
SeleniumWebDriverFactory selFac = new SeleniumWebDriverFactory(rmDriver);
}
also, i assume you're injecting RemoteWebDriver rmDriver;
Since the method is static, you don't need an object to call it.
SeleniumWebDriverFactory.getTestContext(ZucchiniConstants.allBrowsers);
As an alternative to creating a constructor you can also do this
public class DevPortalTest extends Test {
RemoteWebDriver rmDriver ;
SeleniumWebDriverFactory selFac;
// this code block runs before constructor
{
try{
selFac = new SeleniumWebDriverFactory(rmDriver);
}catch(Exception e){
// handle exception
}
}
Static method can be accessed using class Name so there is no need to create any Object in Abstract class.
I want to get my class property with string name.
I have a code like this
class Test
{
public String simple = "hello";
public void getSetting()
{
try
{
Test c = new Test();
Class cls = this.getClass();
Field field = cls.getField("simple");;
}
catch(Exception e)
{
// error
}
}
}
I get an error with this code , because my property is non-static , and when i changing my property to static , it's work fine , how can i get non-static properties with reflection?
Here's a self-contained example on how to get an instance Field through reflection.
public class Main {
// the instance field
String simple = "foo";
// some static main method
public static void main(String[] args) throws Exception {
// initializing the class as we're accessing an instance method
new Main().reflect();
}
public void reflect() {
Class<?> c = this.getClass();
try {
// using getDeclaredField for package-protected / private fields
Field field = c.getDeclaredField("simple");
// printing out field's value for this instance
System.out.println(field.get(this));
}
// TODO handle better
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
catch (NoSuchFieldException n) {
n.printStackTrace();
}
}
}
Output
foo
try
{
Test c = new Test();
Class cls = c.getClass(); //Change this.getClass to c.getClass()
Field field = cls.getField("simple");;
}
The Field must be static or belong to a instance that can be get via reflection.
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();
}
}