编写Java库以有条件地处理输入类w/o,从而导致对该类的无条件运行时依赖

编写Java库以有条件地处理输入类w/o,从而导致对该类的无条件运行时依赖,java,jar,dependencies,classpath,instanceof,Java,Jar,Dependencies,Classpath,Instanceof,我有一项看起来很棘手的Java库任务 我需要编写一个适配器/助手类,用于处理JTables,如果JTable是一个。但是我不想在swingx-core-1.6.2.jar上添加运行时依赖项,除非我的应用程序实际使用JXTable(在这种情况下,它已经要求在类路径上有swingx-jar文件) 如何将代码解耦以实现这一点?我甚至不知道如何测试JXTable;如果我尝试使用JXTable的instanceof作为测试,这意味着我的代码已经对JXTable具有无条件的运行时依赖性 我以前编写过具有“可

我有一项看起来很棘手的Java库任务

我需要编写一个适配器/助手类,用于处理
JTable
s,如果
JTable
是一个。但是我不想在swingx-core-1.6.2.jar上添加运行时依赖项,除非我的应用程序实际使用JXTable(在这种情况下,它已经要求在类路径上有swingx-jar文件)

如何将代码解耦以实现这一点?我甚至不知道如何测试JXTable;如果我尝试使用JXTable的
instanceof
作为测试,这意味着我的代码已经对JXTable具有无条件的运行时依赖性

我以前编写过具有“可选”运行时链接依赖项的Java库:如果我的库中有此项:

package com.foobar.foolib;

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

public class SuperObject
{
    /* ... uses a BloatwareThingy ... */
}
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");
    }
SuperObject
是唯一使用whizbang.jar的类,那么只要我的最终应用程序不使用
SuperObject
,那么就没有对whizbang.jar的运行时依赖;如果我的最终应用程序确实希望使用
SuperObject
,那么它需要在类路径上包含whizbang.jar。从应用程序的角度来看是可选的。效果很好


如果应用程序只使用JTable,我如何编写一个方法来测试作为JXTable实例的给定JTable,而不需要依赖于SwingX jar文件?这需要反射。使用
Class.forName(…)
获取JXTable类的类对象。如果它不存在,它将抛出
ClassNotFoundException


如果存在,您可以使用该类对象获取所需的方法,然后将它们应用于JTable对象。

首先,您要消除的是编译时依赖关系,而不是运行时依赖关系

您可以通过以下方式检查类的类型:

if (table.getClass().getName().equals("path.to.JXTable")
{
// Do something using reflection.
}
else // proceed as normal
您可以使用以下工具进行测试:

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

if( cls != null )
    // have JXTable
之后,您必须将反射专门用于对外部库中的类、方法、构造函数和字段的所有访问

如果您需要以这种方式访问大型API,那么这可能会变得非常笨拙,您可以编写可以直接使用JXTable但通过反射创建并通过接口或抽象类调用的帮助器类:

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文件中。使用这种方法,只有在org.jdesktop.swingx.JXTable可用时,您才会使用JXTableHandler,否则将使用StdTableHandler。

Aha,我有一些工作要做:

我的库中的Helper类:

package com.foobar.foolib;

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

public class SuperObject
{
    /* ... uses a BloatwareThingy ... */
}
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");
    }
我的库中其他地方的用法:

package com.foobar.foolib;

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

public class SuperObject
{
    /* ... uses a BloatwareThingy ... */
}
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,它也可以工作。(如果我在应用程序中使用JXTable而在类路径上不使用swingx,它也会“起作用”,因为应用程序无法实例化JXTable,这是我的库不会改变的预期行为。)

我试图消除的不是编译时依赖性;我希望我的库在编译时依赖于SwingX。我只是不希望运行时依赖于SwingX,除非我的应用程序也依赖于SwingX。在
new JXTableHandler().handleTable(表)中,不能保证在没有NoClassDefFoundErrors的情况下加载此方法中的JXTableHandler。只要它没有依赖于不存在的外部类的变量或内部类,它就应该工作。但是,如果所需的API不可用,我宁愿避免加载这样的帮助器类,以确保在类演化时不会突然出现异常。