Как построить проект Java с Java 6 против библиотек Java 1.4? - PullRequest
6 голосов
/ 05 сентября 2011

У меня есть проект, который был изначально написан для Java 1.4, но у меня есть только Java 6 на моем Mac, и я не могу установить 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.

Похоже, решением было бы получить и скомпилировать JAR-файлы Java 1.4. Итак, у меня есть несколько вопросов:

  1. Есть ли лучший способ?
  2. Как мне указать для моей Java 1.6 javac, что я хотел бы использовать 1.4 JAR вместо JAR Java 6?
  3. Будет ли это работать, и если да, будет ли проект работать на Java 1.4, а также на Java 6?
  4. Как мне это сделать в Maven?

Спасибо!

Ответы [ 3 ]

4 голосов
/ 05 сентября 2011

Ваша ситуация кажется довольно надуманной. Я постараюсь упростить вопросы. Сначала я проигнорирую ваш вопрос о Maven.

Итак, позвольте мне сначала изложить некоторые факты:

-source=1.4 означает: Уважаемый компилятор, пожалуйста, принимайте только языковые конструкции --- не библиотечные функции --- которые были доступны с javac JDK 1.4.

-target=1.4 означает: Уважаемый компилятор, пожалуйста, напишите файлы классов в двоичном формате, совместимом с JRE 1.4.

Я понял, что вас интересует совместимость во время загрузки с JDK 1.4, т. Е. Вы хотите, чтобы файлы классов, созданные в вашей настройке, могли быть загружены JDK 1.4. Это правильно?

Вы также хотите поддерживать совместимость с исходным кодом? То есть Вы хотите, чтобы другие могли скомпилировать ваш код на JDK 1.4?

Если ответ на последний вопрос положительный, я попытался бы установить JDK 1.4 на OS X. Он поддерживает несколько установленных JDK. Так что я уверен, что это возможно. Если это не вариант, используйте:

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

Обратите внимание, не используйте -Xbootclasspath. Это изменяет загрузочный путь к классу jvm, выполняющего javac.

Если ответ на поставленный выше вопрос - нет. Вы можете распоряжаться -source=1.4, позволяющим использовать дженерики и другие улучшения Java 5 в вашем коде. Но вы все равно должны обеспечить двоичную совместимость с помощью:

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

Другой вариант - использовать Retroweaver .

После повторного прочтения вашего вопроса, я хотел бы добавить, что вам нужно заполучить вариант JDK 1.4 с файлами классов jdbc. В противном случае вы столкнетесь с ошибками компилятора, которые вы указали в своем вопросе.

4 голосов
/ 05 сентября 2011

Если вы не являетесь поставщиком JDBC, неразумно реализовывать подобные интерфейсы.

Попробуйте использовать прокси для обеспечения совместимости между версиями 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 относительно дешевое, в старых версиях оно меньше; это может отрицательно сказаться на производительности.

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

Как указать для моего Java 1.6 javac, что я хотел бы использовать 1.4 JAR вместо Java 6 JAR?

В Win.& * nix это будет, указав параметр bootclasspath.Подробнее см. javac: Опции кросс-компиляции .

...