Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java reflection: implement interface and pass to another class

I'm not sure if this is possible with Java, but I'm trying to implement an interface that's unavailable at compile time** and pass it to another class as an object of that interface. Let's say I have an interface like:

public interface MyInterface {
    void onReceive(int i);
}

and another class like:

public void MyClass {
    ArrayList<MyInterface> listenerList = new ArrayList<MyInterface>();

    public void add(MyInterface m) {
        listenerList.add(m);
    }
}

If they were available at compile time, I would be using them like:

blah = new MyInterface() {
    public void onReceive(int i) {
        System.out.println("BLAH");
    }
}

MyClass mc = new MyClass();
myClass.add(blah);

I'm wondering if there is a way to write code that does the same as above if the first two classes are only available at runtime.

Thanks in advance!

**I'm trying to use a framework library from Android's ROM, but it is in dalvik bytecode so I can't use it for compilation.

UPDATE: Here's some sample code I used to test the solution:

File a/IIMSListener.java

// Excerpt from decompiled class

public interface IIMSListener
{
    void onReceive(int p0, int p1/*, IMSParameter p2*/);
}

File a/IMSRemoteListenerStub.java

// Excerpt from decompiled class

import java.util.concurrent.*;
import java.util.*;

public class IMSRemoteListenerStub
{
    public List<IIMSListener> mListenerList = new CopyOnWriteArrayList<IIMSListener>();

    public boolean addListener(final IIMSListener iimsListener) {
        if (iimsListener != null && !this.mListenerList.contains(iimsListener)) {
            this.mListenerList.add(iimsListener);
            return true;
        }
        return false;
    }

    public boolean removeListener(final IIMSListener iimsListener) {
        if (iimsListener != null && this.mListenerList.contains(iimsListener)) {
            this.mListenerList.remove(iimsListener);
            return true;
        }
        return false;
    }
}

File b/test.java

import java.lang.reflect.; import java.util.;

public class test {
  public static void main(String[] args) throws IllegalAccessException,
                                                IllegalArgumentException,
                                                InvocationTargetException,
                                                NoSuchMethodException,
                                                SecurityException,
                                                ClassNotFoundException {

    // Implement interface
    Class<?> IIMSListener = Class.forName("IIMSListener");

    Object listenerInstance = Proxy.newProxyInstance(IIMSListener.getClassLoader(), new Class<?>[]{IIMSListener}, new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("onReceive")){
          System.out.println("ARGS: " + (Integer)args[0] + ", " + (Integer)args[1]);
          return 1;
        }
        else return -1;
      }
    }); 

    // Test
    Method onReceive = listenerInstance.getClass().getDeclaredMethod("onReceive", new Class[] { int.class, int.class });
    onReceive.invoke(listenerInstance, new Object[] { 1, 2 });

    try {
      // Pass to another class
      Class IMSRemoteListenerStub = Class.forName("IMSRemoteListenerStub");
      Constructor ctor = IMSRemoteListenerStub.getConstructor();
      Object stubInstance = ctor.newInstance(new Object[] {});
      Method addListener = stubInstance.getClass().getDeclaredMethod("addListener", new Class[] { IIMSListener });
      addListener.invoke(stubInstance, new Object[] { listenerInstance });

      // Test
      Field mListenerList = IMSRemoteListenerStub.getField("mListenerList");
      List<?> list = (List<?>)mListenerList.get(stubInstance);
      onReceive.invoke(list.get(0), new Object[] { 3, 4 });
    }
    catch (InstantiationException e) {}
    catch (NoSuchFieldException e) {}
  }
}

Execution:

$ cd b
$ CLASSPATH=".:../a" java test
ARGS: 1, 2
ARGS: 3, 4
like image 423
Andrew Gunnerson Avatar asked Oct 18 '13 01:10

Andrew Gunnerson


People also ask

How do you implement an interface from another class in Java?

The implements keyword is used to implement an interface . The interface keyword is used to declare a special type of class that only contains abstract methods. To access the interface methods, the interface must be "implemented" (kinda like inherited) by another class with the implements keyword (instead of extends ).

How is reflection implemented in Java?

Using Arrays. One final use of reflection is in creating and manipulating arrays. Arrays in the Java language are a specialized type of class, and an array reference can be assigned to an Object reference. This example creates a 10-long array of Strings, and then sets location 5 in the array to a string value.

Can interface implements another class?

An interface can extend any number of interfaces but one interface cannot implement another interface, because if any interface is implemented then its methods must be defined and interface never has the definition of any method.

How do you create an instance of a class using reflection?

We can use newInstance() method on the constructor object to instantiate a new instance of the class. Since we use reflection when we don't have the classes information at compile time, we can assign it to Object and then further use reflection to access it's fields and invoke it's methods.


1 Answers

If it is going to be same interface then use Dynamic Proxies

//Loading the class at runtime
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
    Class<?> someInterface = Class.forName("SomeInterface");

    Object instance = Proxy.newProxyInstance(someInterface.getClassLoader(), new Class<?>[]{someInterface}, new InvocationHandler() {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            //Handle the invocations
            if(method.getName().equals("someMethod")){
                return 1;
            }
            else return -1;
        }
    }); 
    System.out.println(instance.getClass().getDeclaredMethod("someMethod", (Class<?>[])null).invoke(instance, new Object[]{}));
}
like image 133
Narendra Pathai Avatar answered Sep 21 '22 11:09

Narendra Pathai