Написание библиотеки Java для условной обработки входного класса без указания безусловной зависимости времени выполнения от этого класса - PullRequest
5 голосов
/ 02 декабря 2011

У меня есть что-то вроде сложной задачи библиотеки Java.

Мне нужно написать класс адаптера / помощника для работы с JTable s, который имеет некоторые дополнительные функции, если JTable это JXTable. Но я не хочу добавлять зависимость времени выполнения от swingx-core-1.6.2.jar , если мое приложение фактически не использует JXTable (в этом случае оно уже требует наличия файла jar SwingX в пути к классам)

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

Я уже писал библиотеки Java, у которых есть «необязательные» зависимости связывания во время выполнения: если у меня есть это в моей библиотеке:

package com.foobar.foolib;

// import from whizbang.jar
import com.whizbang.BloatwareThingy;

public class SuperObject
{
    /* ... uses a BloatwareThingy ... */
}

и SuperObject - единственный класс, который использует whizbang.jar, тогда, пока мое конечное приложение не использует SuperObject, тогда нет никакой зависимости во время выполнения от whizbang.jar; если мое конечное приложение хочет использовать SuperObject, то оно должно включить whizbang.jar в classpath. Необязательно с точки зрения приложения. Прекрасно работает.

Как мне написать метод для проверки того, что JTable является экземпляром JXTable, не требуя зависимости от файла jar SwingX, если приложение использует только JTable?

Ответы [ 4 ]

5 голосов
/ 02 декабря 2011

Вы можете проверить с помощью:

Class cls = null;
try {
    cls = Class.forName( "org.jdesktop.swingx.JXTable" );
} catch( Throwable ex ) {}

if( cls != null )
    // have JXTable

После этого вам придется использовать Reflection исключительно для всех обращений к классам, методам, конструкторам и полям из этой внешней библиотеки.

Поскольку это может стать очень неуклюжим, если вам нужен такой большой API-интерфейс, вы можете написать вспомогательные классы, которые могут напрямую использовать JXTable, но создаются через Reflection и вызываться через интерфейсы или абстрактные классы:

public interface MyTableHandler
{
    void doSomethingWithTable( JTable table );
}

public class JXTableHandler implements MyTableHandler
{
    void doSomethingWithTable( JTable table )
    {
        JXTable jxt = (JXTable) table;
        // use JXTable API directly ...
    }
}

public class StdTableHandler implements MyTableHandler
{
    void doSomethingWithTable( JTable table )
    {
        // do without JXTable
    }
}

public class MyIndependentClass
{
    static final MyTableHandler handler;

    static {
        try {
            Class.forName( "org.jdesktop.swingx.JXTable" ); // force exception
            handler = (MyTableHandler) Class.forName( "my.pkg.JXTableHelper" )
                                            .newInstance();
        } catch( Throwable ex ) {
            handler = new StdTableHandler();
        }
    }
    public void treatTable( JTable table )
    {
        handler.doSomethingWithTable( table );
    }
}

У Java VM нет проблем с использованием несуществующего API в классах, которые не используются сами по себе, но присутствуют только в jar-файлах. При таком подходе вы будете использовать JXTableHandler, только если org.jdesktop.swingx.JXTable доступен, а StdTableHandler в противном случае.

2 голосов
/ 02 декабря 2011

Это требует отражения.Используйте Class.forName(...) для извлечения объекта Class для класса JXTable.Если его нет, он выдаст ClassNotFoundException.

. Если он присутствует, вы можете использовать этот объект Class, чтобы получить нужные вам методы, а затем применить их к объекту JTable.

0 голосов
/ 02 декабря 2011

Ага, у меня есть что-то для работы:

вспомогательный класс в моей библиотеке:

static private class JXTableHandler
{
    public JXTableHandler() {}

    boolean testJXTable(JTable table)
    {
        try
        {
            if (table instanceof JXTable)
                return true;
        }
        catch (java.lang.NoClassDefFoundError e) { /*gulp*/ }
        return false;
    }
    boolean handleTable(JTable table)
    {
        if (!testJXTable(table))
            return false;

        JXTable xtable = (JXTable) table;
        // here's where we do stuff, this is just something basic
        System.out.println("columnControlVisible: "
             +xtable.isColumnControlVisible());
        return true;
    }
}

Использование в другом месте в моей библиотеке:

    JComponent c = ...

    if (c instanceof JTable)
    {
        JTable table = (JTable) c;
        boolean isJXTable = new JXTableHandler().handleTable(table);
        System.out.println("jtable "+
            (isJXTable ? "is" : "is not")+" a jxtable");
    }

Я однажды запустил приложение с ним, используя только JTables, без swingx на моем пути к классам, и это работает, потому что оно перехватывает NoClassDefFoundError, и в этом случае оно никогда не пытается получить доступ к классу JXTable. Если я использую JXTable в своем приложении с swingx на моем classpath, это также работает. (и это также «работает», если я использую JXTable в своем приложении без swingx на моем пути к классам, потому что приложение не может создать экземпляр JXTable, ожидаемое поведение которого не изменится моей библиотекой.)

0 голосов
/ 02 декабря 2011

Прежде всего, вы пытаетесь устранить зависимость времени компиляции, а не время выполнения.

Вы можете проверить тип класса через

if (table.getClass().getName().equals("path.to.JXTable")
{
// Do something using reflection.
}
else // proceed as normal
...