4

对于我的 Swing 项目,我需要同时支持Java 5Java 6。我已经定义了一个自定义JComponent(调用它Picture),并且在将它嵌入到 a 之后JScrollPane,我将它放入JPanel使用 DesignGridLayout 管理器的 a 中。

得益于swing-layout开源库,DesignGridLayout 支持基线对齐(实现对 Java 5 的基线支持并提供与新的 Java 6 基线支持的兼容性)。

我的Picture覆盖 public int getBaseline(int width, int height),以便我可以为它定义一个正确的基线。请注意,“覆盖”并不完全正确:它覆盖了 Java6 上的方法,但在 Java5 中定义了它。

当我在 Java5 上运行我的示例应用程序时,一切都很好Picture我定义的基线被正确使用。

但是,当我使用 Java6 时,我的Picture#getBaseline()方法不会被调用!当然,我的图片的基线对齐很糟糕(居中)。

在检查 Java6 源代码后,我看到,在 中BasicScrollPaneUIgetBaseline()首先调用getBaselineResizeBehavior()视口组件(我的Picture实例)。并且getBaseline()只有在getBaselineResizeBehavior()返回时才会调用Component.BaselineResizeBehavior.CONSTANT_ASCENT

现在我的问题是这是我无法在 Java5 中实现getBaselineResizeBehavior()的 Java6 方法,因为它返回 Java5中不存在的枚举。JComponentComponent.BaselineResizeBehavior

所以我的问题(最后)是:我如何实现(或模拟?)getBaselineResizeBehavior()以便我的类仍然可以在 Java5 环境中编译和运行?

4

4 回答 4

2

如何实现(或模拟?) getBaselineResizeBehavior() 以便我的类仍然可以在 Java5 环境中编译和运行?

您无法使用 Java 5 库编译此方法声明,因为类型Component.BaselineResizeBehaviour不存在:

public Component.BaselineResizeBehavior getBaselineResizeBehavior()

您必须使用 Java 6 进行编译。如果您编译到 1.5 目标,您的类仍然可以在 Java 5 上运行,但您必须注意它们可以优雅地处理缺失的类型/方法。在遇到这些情况时为它们添加测试。确保开发人员在签入之前尝试在 Java 5 上运行他们的代码。

比如这个类...

public class MyPanel extends javax.swing.JPanel {

    public java.awt.Component.BaselineResizeBehavior getBaselineResizeBehavior() {
        return java.awt.Component.BaselineResizeBehavior.OTHER;
    }

    public static void main(String[] args) {
        new MyPanel();
        System.out.println("OK");
    }

}

...可以使用javac JDK 编译器按如下方式编译和运行:

X:\fallback>javac -version
javac 1.6.0_05

X:\fallback>javac -target 1.5 MyPanel.java

X:\fallback>"C:\Program Files\Java\jre1.5.0_10\bin\java.exe" -cp . MyPanel
OK

所有流行的 IDE 都提供了生成旧类版本的选项。当您需要决定代码路径时,您可以使用反射在运行时测试方法/类型是否存在。

未能设置目标将导致如下错误:

Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version n
umber in .class file
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$100(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClassInternal(Unknown Source)
于 2009-01-05T10:13:55.210 回答
2

我会创建一个 Picture 的子类,可能称为 PictureJava6,它实现了 getBaselineResizeBehaviour(),并且在创建 Picture 实例时,执行以下操作:

public Component pictureFactory() {
    if(javaVersion > "1.6") {
        return new PictureJava6();
    } else {
        return new Picture();
    }
}
于 2009-01-05T11:10:54.563 回答
0

您可以使用反射尝试按名称获取 CONSTANT_ASCENT 返回值。如果不能反映,你是J5,否则是J6。这避开了显式依赖并允许编译到 J5。

跟随。是为对话模式执行此操作的示例:

try {
    Field  fld=Class.forName("java.awt.Dialog$ModalExclusionType").getField("TOOLKIT_EXCLUDE");
    Method mth=getClass().getMethod("setModalExclusionType",new Class[]{fld.getType()});
    mth.invoke(this,new Object[]{fld.get(null)});
    }
catch(Throwable thr) {
    log.errorln("Unable to configure window to be unaffected by modal dialogs - dialogs may need to be closed to operate help.");
    log.errorln("Use Java 6 or later to avoid modal dialogs conflicting with the help system.");
    log.errorln("Exception: "+thr);
    }

更新:我最初发布了 J5 代码注释掉的代码;我已经改变了这一点,因为我意识到它暗示 J5 代码在 J6 中不起作用,从而混淆了问题——它确实如此。

于 2009-01-05T00:40:49.547 回答
0

我认为在解析虚函数和重载时,返回类型不被视为方法签名的一部分;可能是您可以定义“覆盖”方法以返回 Object,并根据我的第一个答案反映返回 Enum。由于您在 J5 中进行编译,因此不会出现编译时冲突,但 JVM 仍应选择您的方法来覆盖......它可能会或可能会引发运行时异常。尽管如此,还是值得一试。

例如:

public Object getBaselineResizeBehavior() {
    Object ret;
    // reflect out the return value
    return ret;
    }  

任何错误处理都可以是 System.out,纯粹用于调试,因为除非您是 J6,否则不会调用它,因此如果调用,正确编码的反射应该始终有效。

当然,我会评论这种方法,以明确发生了什么。

于 2009-01-05T02:19:32.857 回答