7

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
3
  • Is it always going to be same interface or interface is going to change? Commented Oct 18, 2013 at 1:40
  • 1
    If you don't know the types at compile time, you will have to use reflection to get a list of constructors and methods. You'll then need to find a way to resolve any arguments so that you can invoke any of the methods or constructors. All in all, not a simple task. You can use JDK proxies to generate interface instances with an InvocationHandler, but it won't be much use without the a variable of that type or reflection. Commented Oct 18, 2013 at 1:43
  • @Liuguanghua English man. Commented Oct 18, 2013 at 1:46

1 Answer 1

12

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[]{}));
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the answer! The interface never changes. The one thing I'm wondering is: if SomeInterface is not available at compile time, how can it be used as a type for the line: SomeInterface instance = ...?
@ChenXiao-Long Ohh yeah that was an error. I have edited the answer to totally use reflection. Take a look. Nice catch though.
But just a general wondering how will you plug in the classes that you are loading and whose interface you dont know. I mean you will have to use reflection all the way.
To give a little more background, I'm trying to make an Android IMS protocol library from the manufacturer firmware work with AOSP. I can decompile the library to see the classes and interfaces to see what functions I'm dealing with. EDIT: By the way, your solution works perfectly! I'll update my question with some sample code I tested.
So in that case this can help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.