I'm making a kind of test program to be able to override methods in classes for an api I'm making in java but I'm getting a weird error when trying to invoke a method from another class...
Here is the main "component class":
package st.cmp;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Component {
public class Overrider{
Class<?> source;
Class<?>[] overs;
String name;
public Overrider(Class<?> s,String n,Class<?>[] o){
source=s;
overs=o;
name=n;
}
public Object call(Object[] param){
try {
return source.getMethod(name, overs).invoke(this, param);
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
};
public HashMap<String,Component> cmps;
public HashMap<String,Overrider> over;
public Component(){
cmps=new HashMap<String,Component>();
over=new HashMap<String, Overrider>();
}
public void registerComponent(String nm,Component cm){
cmps.put(nm,cm);
}
public Component getComponent(String nm){
return cmps.get(nm);
}
public void override(Class<?> cl,String name,Class<?>[] param){
over.put(name,new Overrider(cl,name,param));
}
public Object call(String mnm,Object[] a){
Overrider ov=over.get(mnm);
if(ov!=null){
ov.call(a);
}
Class<?>[] abc=new Class<?>[a.length];
for(int i=0;i<a.length;i++){
abc[i]=a[i].getClass();
}
try {
return this.getClass().getDeclaredMethod(mnm, abc).invoke(this,a);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException
| SecurityException e) {
// TODO Auto-generated catch block
try {
this.getClass().getDeclaredMethod(mnm, abc).invoke(this,a);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException
| SecurityException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
return null;
}
public void test(String a){
System.out.print(a);
}
public int add(Integer a,Integer b){
return a+b;
}
}
And this is the main class:
package st;
import st.cmp.Component;
public class Start {
public static void main(String[] args)
{
new Start().start();
}
public void start(){
Component a=new Component();
a.call("test",new Object[]{a.call("add",new Object[]{1,5}).toString()});
a.override(this.getClass(), "add", new Class<?>[]{Integer.class,Integer.class});
a.call("test",new Object[]{a.call("add",new Object[]{1,5}).toString()});
}
public int add(Integer a,Integer b){
return a*b;
}
}
I'm getting this error when I start the program:
6java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at st.cmp.Component$Overrider.call(Component.java:22)
at st.cmp.Component.call(Component.java:64)
at st.Start.start(Start.java:16)
at st.Start.main(Start.java:8)
6
Can anyone help me?
it says "object is not an instance of declaring class"... But what "object" is it refering to?
In your Start class, you are calling the Component.override() method:
// v--- This is the Class Object
a.override(this.getClass(), "add", new Class<?>[]{Integer.class,Integer.class});
Where a is of type Component. You are passing it this.getClass(), which is a Class object for Start. Then in override():
// v--- Class object gets passed along here
over.put(name,new Overrider(cl,name,param));
You are creating a new Overrider, and giving the Class object for Start to the constructor, which sets the Class<?> source; field to the Start Class object. Then when you call the Overrider.call() method, it does this:
// v--- and finally invoked here
return source.getMethod(name, overs).invoke(this, param);
And passes invoke() a this which is an instance of Component, while source is a Class object for Start. In this line, "source" and "this" need to be the same class, but Start and Component aren't.
Your code is quite convoluted, but this my help. If you have a line like this:
klass.getMethod(name).invoke(obj)
then the error says that obj is not an instance of klass.
Related
Lets say I have the following setup
an interface
public interface TestInterface {
public void draw();
}
and two implementations
public class Square implements TestInterface {
#Override
public void draw() {
System.out.println("Square");
}
}
and
public class Circle implements TestInterface {
#Override
public void draw() {
System.out.println("Circle");
}
}
Now I can easily do
TestInterface a = new Square();
a.draw();
and I correctly get Square. Next, I wanted to try out reflection.
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Constructor<?> constructor = null;
try {
constructor = clazz.getConstructor();
} catch (NoSuchMethodException | SecurityException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Object instance = null;
try {
instance = constructor.newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Method method = null;
try {
method = instance.getClass().getMethod(methodName);
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
method.invoke(instance);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
and for className as some.package.Square and methodName as draw I again get Square. Which is correct.
The problem is that my application has access to the interface but not the actual implementations. Hence I know which methods to invoke, but I have to specify the implemented classes as well and I dont know which package they may reside in. What if I only know the name of the class but not the package?
Is there a way where I can still use the initialization form of
TestInterface a = new <some_parameter>();
a.draw();
Is there a way to generalize it? Or is the approach using reflection that I showed above, the only way to achieve something like this? Lastly, would it make any difference if I used an abstract class instead of an interface?
You need to pass:
#param className the fully qualified name of the desired class.
When you have for example three class with the Same name but in diferent packages
--package1
----Test
--package2
----Test
Main
Test
And in Main you have:
public static void main(String[] args) throws UnsupportedEncodingException {
Class<?> clazz = null;
try {
clazz = Class.forName("Test");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
It will call the one that is level of Main. For calling the others you will need to pass the fully quallifed name.
clazz = Class.forName("package1.Test");
clazz = Class.forName("package2.Test");
What if I only know the name of the class but not the package? So you need to know in what level you want. Because as you know that different packages the classes can have same names. So which Class do you need if you have that issue.
You have to know the full name of the class. Only the name of the class is not enough to load it in the memory and to use it through reflection. However, you can determine the implementations of your interface using the reflections library.
Maybe this discussion thread can help you: How can I get a list of all the implementations of an interface programmatically in Java?
How can i call registerAudioPortUpdateListener?
I succeeded to call a hidden function.
But, in this situation i need to call a function with hidden inner interface as parameter.
public class AudioManager {
/**
* Listener registered by client to be notified upon new audio port connections,
* disconnections or attributes update.
* #hide
*/
public interface OnAudioPortUpdateListener {
/**
* Callback method called upon audio port list update.
* #param portList the updated list of audio ports
*/
public void onAudioPortListUpdate(AudioPort[] portList);
/**
* Callback method called upon audio patch list update.
* #param patchList the updated list of audio patches
*/
public void onAudioPatchListUpdate(AudioPatch[] patchList);
/**
* Callback method called when the mediaserver dies
*/
public void onServiceDied();
}
/**
* Register an audio port list update listener.
* #hide
*/
public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
sAudioPortEventHandler.init();
sAudioPortEventHandler.registerListener(l);
}
}
Can you try this code , for me it is working in java,
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import de.apps.io.AudioManager;
public class Application {
public static void main(String[] args) {
System.out.println("hello world");
Class someInterface1 = null;
try {
// someInterface = AudioManager.class.getDeclaredClasses();
someInterface1 = Class.forName("de.apps.io.AudioManager$OnAudioPortUpdateListener");
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println(someInterface);
System.out.println(someInterface1);
Object o = Proxy.newProxyInstance(someInterface1.getClassLoader(), new java.lang.Class[] { someInterface1 }, new Handler());
AudioManager manager = new AudioManager();
Method me = null;
try {
me = manager.getClass().getMethod("registerAudioPortUpdateListener", new java.lang.Class[] { someInterface1 });
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
me.invoke(manager, o);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(me);
}
static class Handler implements InvocationHandler
{
#Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String method_name = method.getName();
Class<?>[] classes = method.getParameterTypes();
System.out.println("called " + method_name);
return null;
}
}
}
Code Sample :
Test1 class would be a parent class which would be responsible to execute Test2 class method
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.testng.annotations.Test;
public class Test1 {
#Test
public void test() {
Class test2 = Test2.class;
Method method = null;
Object obj = null;
try {
method = test2.getMethod("test", String.class);
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
method.setAccessible(true);
try {
obj = method.invoke(test2.newInstance(), "0");
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Test2.class code :
import org.testng.Assert;
public class Test2 {
public void test(String obj){
Assert.assertEquals(obj, "1");
}
}
Here as per this example , assertion should fail , but testng report states as passed
So how do i link assertion failure to the testng report
I think it would be better to isolate the handler of InvocationTargetException in a single catch block and check the result using getCause(). Here is what I tried;
try {
Object obj = method.invoke(test2obj, "0");
} catch( InvocationTargetException e){
Throwable e1 = e.getCause();
System.out.println("Method invocation failed... due to " + e1);
} catch (IllegalAccessException | IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And I got this message;
Method invocation failed... due to org.junit.ComparisonFailure: expected:<[0]> but was:<[1]>
Here is my classes.
package com.psu.janibot;
public class Janibot implements Runnable {
public void move() {
System.out.println("move");
}
#Override
public void run() {
}
// main method
public static void main(String[] args) {
try {
Janibot janibot = (Janibot) Class.forName("Janitor").newInstance();
janibot.run();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
import com.psu.janibot.Janibot;
public class Janitor extends Janibot {
public void run() {
move();
}
}
package com.psu.janibot;
public class Janitor2 extends Janibot{
public void run() {
move();
}
}
if i type >java Janitor it will run the Janitor class
if i type >java Janitor2 it will run the Janitor2 class
I want to do is to run Janitor or Janitor2 without typing the class name in forName method like this Janibot janibot = (Janibot) Class.forName("Janitor").newInstance();
Class.forName() takes a String as a parameter, so you need to pass it somehow, so I presume you mean not hard coded.
Pass the String in as the arguments to the program and read them from the args array
java Janibot Janitor
....
Class.forName(args[0]).newInstance()
Other options exist such as read from file, using a Scanner, etc.
Below is the code snippet, I am trying to invoke the usingClass method using REFLECTION. Calling the usingClass() method directly(w/o reflection) works when I pass an object of type Child, though when I try to achieve the same thing using Reflection it throws NoSuchMethodFoundException. Would like to understand if I am missing something or is there any logic behind this? Please help
package Reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMethodInvocation {
/**
* #param args
*/
public static void main(String[] args) {
TestMethodInvocation test = new TestMethodInvocation();
Child child = new Child();
Parent parent = (Parent)child;
Class<? extends Parent> argClassType = parent.getClass();
Class<? extends TestMethodInvocation> thisClassType = test.getClass();
test.usingClass(child);
Method methodToCall;
try {
methodToCall = thisClassType.getDeclaredMethod("usingClass", argClassType);
methodToCall.invoke(test, parent);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void usingClass(Parent p){
System.out.println("UsingClass: " + p.getClass());
}
}
Output is as below.
UsingClass: class Reflection.Child
java.lang.NoSuchMethodException: Reflection.TestMethodInvocation.usingClass(Reflection.Child)
at java.lang.Class.getDeclaredMethod(Unknown Source)
at Reflection.TestMethodInvocation.main(TestMethodInvocation.java:20)
The reason your code does not work is that getClass() is dynamically bound. Casting to Parent does not affect the runtime type of your object and so the variables child and parent contain the same class object.
Unless you explicitly query your instance for its parent class via getGenericSuperclass() or something similar, you will have to use the static way mentioned by dystroy.
You should use
methodToCall = thisClassType.getDeclaredMethod("usingClass", Parent.class);
because the precise exact class of parent (which is Child), is used at runtime and the type of the variable holding it changes nothing.
Another (too heavy) way to solve it would be :
Class<? extends Parent> argClassType2 = (new Parent()).getClass();
...
methodToCall = thisClassType.getDeclaredMethod("usingClass", argClassType2);