如何使用Java 6针对Java 1.4库构建Java项目?

时间:2011-09-05 12:12:14

标签: java macos maven

我有一个最初是为Java 1.4编写的项目,但我的Mac上只有Java 6,我无法安装Java 1.4。

通常情况下,我会使用这样的一行来编译:

javac -source=1.4 -target=1.4 MyClass.java

但是,MyClass.java实现了java.sql.ResultSet接口,它在Java 6中添加了几个新方法,因此我遇到了编译错误,如:

MyClass is not abstract and does not override abstract method
updateNClob(java.lang.String,java.io.Reader) in java.sql.ResultSet

我不能简单地实现缺少的方法,因为许多使用泛型,这在Java 1.4中是不可用的。

似乎解决方案是获取并编译Java 1.4 JAR。所以,我有几个问题:

  1. 有更好的方法吗?
  2. 如何向Java 1.6 javac指定我想使用1.4 JAR而不是Java 6 JAR?
  3. 这是否会起作用,如果是这样,项目是否会在Java 1.4以及Java 6上运行?
  4. 我如何在Maven中执行此操作?
  5. 谢谢!

3 个答案:

答案 0 :(得分:4)

除非您是JDBC供应商,否则实现像这样的接口是不明智的。

考虑使用proxy来维护JVM版本之间的兼容性。


迁移到代理服务器如下完成。考虑这个ResultSet实现:

public class ResultSetFoo implements ResultSet {

  public String getString(int columnIndex) throws SQLException {
    return "foobar";
  }

  // other Java 1.4 methods

这将被更改,因此没有类实现ResultSet

public class ResultBar {

  public String getString(int columnIndex) throws SQLException {
    return "foobar";
  }

  // other method signatures matching the 1.4 ResultSet, as before

然后,您需要在运行时构建两种类型之间的方法映射(鸭子类型的原始形式:)

  private static final Map RESULT_SET_DUCK = initResultSet();

  private static Map initResultSet() {
    Map map = new HashMap();
    Method[] methods = ResultSet.class.getMethods();
    for (int i = 0; i < methods.length; i++) {
      try {
        Method match =
            ResultBar.class.getMethod(methods[i].getName(),
                methods[i].getParameterTypes());
        map.put(methods[i], match);
      } catch (SecurityException e) {
        throw new IllegalStateException(e);
      } catch (NoSuchMethodException e) {
        // OK; not supported in 1.4
      }
    }
    return map;
  }

这允许您通过代理调用ResultBar类型:

  /** Create a java.sql.ResultSet proxy */
  public static ResultSet proxy(final ResultBar duck) {
    class Handler implements InvocationHandler {
      public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable {
        Method proxiedMethod = (Method) RESULT_SET_DUCK.get(method);
        if (proxiedMethod == null) {
          throw new UnsupportedOperationException("TODO: method detail");
        } else {
          return invoke(proxiedMethod, duck, args);
        }
      }

      private Object invoke(Method m, Object target, Object[] args)
          throws Throwable {
        try {
          return m.invoke(target, args);
        } catch (InvocationTargetException e) {
          throw e.getCause();
        }
      }
    }

    return (ResultSet) Proxy.newProxyInstance(null, RSET, new Handler());
  }

这样的实现应该允许在一个JVM中编译的代码在未来的JVM中使用,即使添加了新方法。 现有方法签名不太可能改变,因为让数据库供应商做一些工作是一回事;让所有API消费者都改变的其他因素。

您可能需要更改类实例的创建方式。您不能再直接使用构造函数:

ResultSet nonPortable = new ResultSetFoo();
//becomes...
ResultSet portable = proxy(new ResultBar());

如果您已经雇用了工厂/建筑商/等。模式这个很容易。

尽管在最新的JVM中反射相对便宜,但在旧版本中反射较少;这可能会对绩效产生不利影响。

答案 1 :(得分:4)

你的情况似乎很人为。我会尽力简化问题。首先,我将忽略你关于Maven的问题。

所以让我首先陈述一些事实:

-source=1.4表示:亲爱的编译器,请仅接受语言结构 ---不是库功能---这些功能可以在JDK 1.4的javac中使用。

-target=1.4表示:亲爱的编译器,请以二进制文件格式编写类文件,该格式与JRE 1.4兼容。

我认为您对与JDK 1.4的加载时兼容性感兴趣,即您希望JDK 1.4可以加载您的设置中生成的类文件。是吗?

您是否也想支持源兼容性?即你想让其他人在JDK 1.4上编译你的代码吗?

如果上一个问题的答案是肯定的,我会尝试在OS X上安装JDK 1.4。它支持多个已安装的JDK。所以我很确定这是可能的。如果没有选择,请使用:

-source=1.4 -target=1.4 -bootclasspath=[path/to/1.4.jar]

注意,不要使用-Xbootclasspath。这会更改执行javac的jvm的引导类路径。

如果上述问题的答案是否定的。您可以处置-source=1.4,允许您在代码中使用泛型和其他Java 5增强功能。但是您仍然必须使用以下方式提供二进制兼容性:

-target=1.4  -bootclasspath=[path/to/1.4.jar]

另一种选择是使用Retroweaver

重新阅读你的问题之后,我想补充说你必须掌握jdbc类文件的JDK 1.4变体。否则,您将遇到问题中显示的编译器错误。

答案 2 :(得分:2)

  

如何向Java 1.6 javac指定我想使用1.4 JAR而不是Java 6 JAR?

胜利。 &安培; * nix将指定bootclasspath选项。有关详细信息,请参阅javac: Cross-Compilation Options