Can I pass parameters to my dynamically created classes? - java

I am successfully loading a class from an external MyClass.class file at runtime, and am able to call it's methods assuming I know the name of them, which I do.
The problem I'm having is I cannot figure out how to pass a parameter to the constructor of the class I am loading.
How can I modify this to pass a parameter to MyClass's constructor? Also, how can I access MyClass's public variable: infoToAccess?
Here are the files I'm working with. (Keep in mind, ClassLoaderExample.pde was written for Processing, but other than sketchPath("") and the lack of a main function, it is identical.
ClassLoaderExample.pde: The main file that loads the class:
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.lang.ClassLoader;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/////// FILE STRUCTURE /////
// ClassLoaderExample //
// --this file //
// --tmp //
// ----MyClass.class //
////////////////////////////
// TODO
// Send parameter to MyClass constructor (by reference)
// Get public int number from MyClass
void setup()
{
String className = "MyClass";
Object instance;
Method updateMethod;
// sketchPath("") returns the directory this file is in
File file = new File(sketchPath(""));
try
{
URL url = file.toURL();
URL[] urls = new URL[]{url};
ClassLoader classLoader = new URLClassLoader(urls);
Class<?> loadedClass = classLoader.loadClass("tmp."+className);
try
{
instance = loadedClass.newInstance();
updateMethod = loadedClass.getDeclaredMethod("update");
// Calls MyClass's update() method
try
{
updateMethod.invoke(instance);
}
catch (InvocationTargetException e) {System.out.println(e);}
catch (IllegalAccessException e) {System.out.println(e);}
}
catch (InstantiationException e) {System.out.println(e);}
catch (IllegalAccessException e) {System.out.println(e);}
catch (NoSuchMethodException e) {System.out.println(e);}
}
catch (MalformedURLException e) {System.out.println(e);}
catch (ClassNotFoundException e) {System.out.println(e);}
}
MyClass.java: The class I am loading:
package tmp;
public class MyClass
{
public int infoToAccess = 1337;
public MyClass(int i)
{
System.out.println("MyClass constructor was called with numebr: " + i);
}
public void update()
{
System.out.println("Update was called.");
}
}
Thanks for any help with this!

you can use the class you loaded to get hold of the constructor
you need an use that to create a new instance.
import java.lang.reflect.Constructor;
...
Class<?> loadedClass = classLoader.loadClass("tmp."+className);
try {
Constructor<?> ctor=loadedClass.getConstructor(int.class);
ctor.newInstance(42);
...
}
...

You need to use reflection for both of the things you asked about:
Class<?> loadedClass = classLoader.loadClass("tmp." + className);
Object oInstance = null;
try {
Constructor<?> c = loadedClass.getConstructor(Integer.class);
oInstance = c.newInstance(10);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
//Handle the possibility that you cannot access this constructor
}
To access the public field of your object:
try {
Field field = loadedClass.getField("infoToAccess");
int fieldValue = field.getInt(oInstance);
} catch (NoSuchFieldException | IllegalAccessException e) {
//Handle the possibility that you cannot access this field
}

As mentioned in the link I wrote in the comments, you can pass parameters in this call
updateMethod = loadedClass.getDeclaredMethod("update", parameters go here);
then you should be able to invoke the method with a parameter(s)

If you want to pass arguments to the constructor you cannot use getInstance(), you have to get the constructor object and pass the parameters there.
Example:
Constructor constructor = aClass.getConstructor(new Class[]{String.class});
MyObject myObject = (MyObject) constructor.newInstance("constructor-arg1");
See tutorial here: Java Reflection - Constructors

Related

Java: check if a class exists and call a specific method if it exists

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
}

How to get objects used in other class in current class using reflection?

I am new in the reflection concept. I have JLabel in one class and it is public, in another class I am getting all public fields and check if it is of type JLabel, I want to change text. For that I am using following code, here I am getting all fields but I cant change the value because the field I am getting from reflection is of type Field I want actual JLabel, if I get it I can change the value of it. Here is my code.
Class clazz = LanguageTranslation.class;
Field[] fields = clazz.getFields();
for(Field f : fields ) {
try {
Class typ = f.getType();
System.out.println("Type is:"+f.getType()+"\t Name:"+f.getName());
if(typ.equals(JLabel.class)) {
/*System.out.println("Field " + f.getName() + " of translation " + languageTranslation + " is a JLabel!");
typ.setText("Hiiiii");*/
System.out.println(f);
typ.setText("Hiiiii");//Setting the text for label but its not working
}
} catch ( SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Can anyone help me in this?
You need LanguageTranslation instance to get the JLabel
LanguageTranslation ltObject=//get it from your app logic
Then you can use the method of Field class
public Object get(Object obj)
You pass the ltObject and obtain the JLabel instance. After casting you can call setText()
UPDATE: After reading commetns. If you have the LanguageTranslation instance you can directly access the public fields (including the JLabel)
typ.setText("Hiiiii");
instead of above line try below line
f.setText("Hiiiii");
package com.test;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.swing.JLabel;
public class LanguageTranslation {
public JLabel test;
public LanguageTranslation() {
test = new JLabel();
test.setText("Original");
}
public static void main(String[] args) {
LanguageTranslation obj = new LanguageTranslation();
System.out.println(obj.test.getText());
Class clazz = LanguageTranslation.class;
Field[] fields = clazz.getFields();
for (Field f : fields) {
try {
Class typ = f.getType();
System.out.println("Type is:" + f.getType() + "\t Name:" + f.getName());
if (typ.equals(JLabel.class)) {
System.out.println(f);
try {
Method m = typ.getMethod("setText", String.class);
System.out.println(m);
m.invoke(obj.test, "Changed Value");
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
System.out.println(obj.test.getText());
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
}

How to map String to Java class? [duplicate]

This question already has answers here:
Loading a Class from a String
(9 answers)
converting a string to a class
(2 answers)
Closed 8 years ago.
I want to map a String into a Java class.
For example, I want to define my Main class such that it works in the following way:
$ java Main SayHello
Hello!
$ java Main SayBye
Bye!
$ java Main SayHola
Error: No such class exists
Here is the code.
public class Main {
public static void main(String[] args) {
// Call args[0].say()
}
}
public class SayHello {
public static void say() {
System.out.println("Hello!");
}
}
public class SayBye {
public static void say() {
System.out.println("Bye!");
}
}
I know that I could do it by manually mapping each possible value of args[0] to a Java class, for example:
if (args[0].equals("SayHello")) {
SayHello.say();
}
But is there a way to do this automatically?
You can do it like that:
public static void main(String[] args) {
final String className = "com.my.package." + args[0];
try {
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod("say");
Object object = clazz.newInstance();
method.invoke(object);
} catch (ClassNotFoundException e) {
System.err.println("Error: No such class exists");
} catch (SecurityException e) {
System.err.println("Error: You are not allowed to do that");
} catch (NoSuchMethodException e) {
System.err.println("Error: No such method exists");
} catch (InstantiationException e) {
System.err.println("Error: Unable to instantiate");
} catch (IllegalAccessException e) {
System.err.println("Error: No access to class definition");
} catch (IllegalArgumentException e) {
System.err.println("Error: Illegal argument");
} catch (InvocationTargetException e) {
System.err.println("Error: Bad target");
}
}
Note: as your say() method is static, you can replace:
Object object = clazz.newInstance();
method.invoke(object);
by:
method.invoke(null);
I found an answer here Creating an instance using the class name and calling constructor
Where you can call class by its name
Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getConstructor(String.class);
Object object = ctor.newInstance(new Object[] { ctorArgument });

ClassCastException when casting Class.forName object to interface implemented by this object

I want to create some plugin example for android, so I have 3 projects for it:
ExternalLibInterface - contains IExternalLib, and builds to externallibinterface.jar file
package com.example.externallibinterface;
public interface IExternalLib {
public String someMethod( String param );
}
ExternalLib - contains externallibinterface.jar and SomeClass implements IExternalLib, builds to externallib.apk
package com.example.externallib;
import com.example.externallibinterface.IExternalLib;
public class SomeClass implements IExternalLib {
public String someMethod(String arg0) {
return arg0;
}
}
SomeApp - contains externallibinterface.jar and class for activity - application where I load external apk and class from it.
import com.example.externallibinterface.IExternalLib;
import dalvik.system.PathClassLoader;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String apkToLoad = null;
String externalPackagePath = "com.example.externallib";
String externalClassPath = "com.example.externallib.SomeClass";
try {
apkToLoad = getPackageManager()
.getApplicationInfo( externalPackagePath, MODE_PRIVATE ).sourceDir;
} catch ( PackageManager.NameNotFoundException e ) {
e.printStackTrace();
}
PathClassLoader pathClassLoader =
new PathClassLoader( apkToLoad,
ClassLoader.getSystemClassLoader() );
try {
Class<?> c = Class.forName( externalClassPath, true, pathClassLoader );
Object someClassInstance = c.newInstance();
//exception ClassCastException here
IExternalLib i = (IExternalLib) someClassInstance;
i.someMethod( "some string" );
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch ( ClassCastException e ) {
e.printStackTrace();
}
}
}
But when I cast Object someClassInstance to IExternalLib I get ClassCastException.
Why? IExternalLib is defined in 3rd place (in externallibinterface.jar).
Try the following:
Class<? extends IExternalLib> l_clazz; // our expected class
Class<?> clazz = Class.forName("com.example.externallib.SomeClass"); // our unknown class
// check if our unknown class can be cast to our expected class
if ((l_clazz = clazz.asSubclass(IExternalLib.class)) != null) {
IExternalLib i = l_clazz.newInstance();
i.someMethod( "some string" );
}
It could happen when different class loaders are loading the class. Make sure to build the whole setup at once so that only one class loader is responsible for loading the classes. It happens frequently when you are just re-deploying a particular .war file onto a existing system. Please see this for more insight can't cast to implemented interface

GWT Server-side reflection

I am trying to use Reflection on the server side only of a GWT app. I have a basic example working in a non-GWT example which can be seen below.
package com.xyz.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class EntryPoint {
/**
* #param args
*/
public static void main(String[] args) {
ClassLoader dynClassLoader = ClassLoader.getSystemClassLoader();
Class<?> dynClass = null;
try {
dynClass = dynClassLoader.loadClass("com.xyz.reflection.RunMe");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Object dynInstance = null;
try {
dynInstance = dynClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Method dynMethod = null;
try {
try {
dynMethod = dynInstance.getClass().getMethod("returnSid",
new Class[] { PassMe.class });
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
String returnValue = (String) dynMethod.invoke(dynInstance,
new Object[] { new PassMe() });
System.out.println("Return Value: " + returnValue.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
using the aptly named:
package com.xyz.reflection;
public class PassMe {
private String sid = "DEFAULT_SID";
public PassMe() {
this.sid = "INITIATED_SID";
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
}
and:
package com.xyz.reflection;
public class RunMe {
public String returnSid(PassMe s) {
return s.getSid();
}
}
This runs fine. When I try running this from a GWT server side class it doesn't work, and instead returns
java.lang.NoSuchMethodException: com.xyz.reflection.RunMe.returnSid(com.xyz.reflection.PassMe)
If I change the parameter to a String (instead of the 'PassMe' class) it works fine. Why does it not like passing my 'PassMe' class? I thought it might be an issue with serialization despite being 100% server code, but I haven't had any luck with this either.
Thanks in advance for any help anyone can give me with this.
In addition to finrod's solution, you can also change your class loader to something like:
ClassLoader dynClassLoader = PassMe.class.getClassLoader();
And you can use the PassMe.class style lookup again. The class used to find a loader doesn't seem to matter though. Just not the system loader.
Weird stuff. I wouldn't doubt if GWT is doing something weird with the class loader though.
Edit: Yep. GWT sets the system class loader to com.google.appengine.tools.development.IsolatedAppClassLoader in dev mode.
I think this could be related to Class loading - but it is just a hunch as I cannot experiment with it in context similar to yours.
Here are some suggestions to try:
You use:
ClassLoader dynClassLoader = ClassLoader.getSystemClassLoader();
dynClass = dynClassLoader.loadClass("com.xyz.reflection.RunMe");
To load the RunMe Class.
However to load the PassMe class you use:
PassMe.class
Try to load the PassMe Class through the dynClassLoader and use that instance in the getMethod() instead of the PassMe.class.
I wonder, do you need to use the dynClassLoader?
This is pretty much a shot in the dark, but does it help if you replace
dynMethod = dynInstance.getClass().getMethod("returnSid",
new Class[] { PassMe.class });
with
dynMethod = dynInstance.getClass().getMethod("returnSid", PassMe.class );
?
It doesn't make a difference outside of a GWT server, but it may exercise the container's VM differently.
Did you put your reflection file in the server side package?
For example:
org.myproject.client - your gwt client package (put here your java files that are able to be complied into java script NO REFLECTION)
org.myproject.server - put here any java files including reflection
org.myproject.shared - put here java classes that are able to be compiled into java script

Categories