Есть ли в Java способ загрузки интерфейса, реализующий класс, который не существует? - PullRequest
3 голосов
/ 12 сентября 2011

Я видел, как Java творит много магии, но возможно ли это сделать:

Во время выполнения, используя (например) ClassLoader.defineClass, загрузить класс A, который реализует интерфейс B. Интерфейс B фактически не существует в пути к классам. Java выдаст исключение (ClassNotFoundException IIRC), и класс не будет загружен. Все остальные части класса A в порядке, и я точно знаю, что ни одна другая часть программы не будет использовать интерфейс B. Итак, я хочу заставить интерпретатор игнорировать отсутствующее определение интерфейса и загрузить класс, полностью совпадающий с A, за исключением того, что он не реализует интерфейс B.

Возможно ли это? Конечно, этого можно достичь, перехватив исключение и вручную отредактировав двоичные данные класса A, а затем снова загрузив его. Или путем создания пустого пустого интерфейса с именем B во время выполнения, вручную создав файл B.class и затем загрузив его. Но это выглядит немного грязно, поэтому мой вопрос: предоставляет ли Java какие-либо удобные способы сделать это?

Если нет, то, я думаю, я попытаюсь реализовать один из этих двух методов, но я все еще хочу услышать мнение.

Я делаю это, чтобы обеспечить удобный способ взаимодействия двух разных баз кода друг с другом, если они оба загружены, и просто работать нормально, если только одна из них.

Ответы [ 3 ]

2 голосов
/ 12 сентября 2011

Мне не известны какие-либо беспорядочные решения для этого, которые работают аналогично тому, что вы описываете.Все сводится к обычаю ClassLoader.Пользовательские ClassLoaders довольно сложны для начала, и если они имеют очень специфическую семантику, подобную этой, то они становятся очень уродливыми.

Не говоря уже о типах проблем, которые вы 'Я столкнусь, если ваш код внезапно не запустится внутри этого ClassLoader.

Я думаю, что вменяемое решение этой проблемы - создать mylibrary.jar иmylibrary-noB.jar (или даже более явно: mylibrary-withB.jar и mylibrary-noB.jar), и пусть пользователи просто выбирают, какой они хотят.

1 голос
/ 12 сентября 2011

Довольно часто включать вторую банку с дополнительными интерфейсами.Вторую банку можно включить, если нужно, и выкинуть, если нет.Если его добавить в конец пути к классу, это произойдет неявно.

0 голосов
/ 12 сентября 2011

В конечном счете, код в записи пути к классу, где определен интерфейс B, должен использовать тот же класс, что и код в записи пути к классу, где определяется класс A. Поэтому определение фиктивного интерфейса не сработает.

В Java вы можете динамически реализовать интерфейс с прокси:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html

Таким образом, в более поздний момент времени, когда ваш интерфейс A будет доступен, вы можете получить его с помощью отражения и создать Proxy, который реализует его методы. Следующий пример показывает это с интерфейсом java.lang.Runnable в качестве интерфейса A:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {

    public static void main(String[] args) throws Exception {
        Class interfaceClass = Class.forName("java.lang.Runnable");

        Object implementingRunnable = Proxy.newProxyInstance(
                    ProxyTest.class.getClassLoader(),
                    new Class[] {interfaceClass},
                    new MyInvocationHandler()
        );

        ((Runnable)implementingRunnable).run();


    }

    static class MyInvocationHandler implements InvocationHandler {

        public Object invoke(Object proxy, Method m, Object[] args)
                throws Throwable {
            System.out.println("called " + m);
            return null;
        }
    }
}

Конечно, в какой-то момент вы фактически должны привести прокси к интерфейсу A. Это необходимо сделать в классе, который определен в той же записи пути к классу, что и интерфейс.

...