Компиляция Java. Есть ли способ сказать компилятору игнорировать части моего кода?

Я поддерживаю приложение Java Swing.

Для обратной совместимости с java 5 (для компьютеров Apple) мы поддерживаем два кодовых файла 1, используя функции Java 6, другие без этих функций.

Код в основном тот же, за исключением 3-4 классов, в которых используются функции Java 6.

Я хочу просто сохранить 1 кодовую базу. Есть ли способ во время компиляции, чтобы заставить компилятор Java 5 "игнорировать" некоторые части моего кода?

Я не хочу просто комментировать/раскомментировать части своего кода, в зависимости от версии моего java-компилятора.

Ответ 1

Предполагая, что классы имеют аналогичную функциональность с 1.5 по сравнению с 6.0 различиями в реализации, вы можете объединить их в один класс. Затем, не редактируя источник, чтобы комментировать/раскомментировать, вы можете положиться на оптимизацию, которую всегда выполняет компилятор. Если выражение if всегда false, код в инструкции if не будет включен в компиляцию.

Вы можете сделать статическую переменную в одном из ваших классов, чтобы определить, какую версию вы хотите запустить:

public static final boolean COMPILED_IN_JAVA_6 = false;

И тогда затронутые классы проверяют статическую переменную и помещают разные разделы кода в простую инструкцию if

if (VersionUtil.COMPILED_IN_JAVA_6) {
  // Java 6 stuff goes here
} else {
  // Java 1.5 stuff goes here
}

Затем, когда вы хотите скомпилировать другую версию, вам просто нужно изменить эту переменную и перекомпилировать. Это может сделать файл java более крупным, но он упрочит ваш код и устранит любое дублирование кода, которое у вас есть. Ваш редактор может жаловаться на недостижимый код или что-то еще, но компилятор должен блаженно игнорировать его.

Ответ 2

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

Решение легко. Потяните затронутые классы в два отдельных независимых проекта - убедитесь, что имена пакетов одинаковы, и просто скомпилируйте их в банки, которые вы затем можете использовать в своем основном проекте. Если вы сохраняете имена пакетов одинаковыми, а сигнатуры методов одинаковы, нет проблем - просто отбросьте ту версию, которая вам нужна, в развертывание script. Я предполагаю, что вы запускаете отдельные скрипты сборки или имеете отдельные цели в одном и том же script - ant, а maven может легко обрабатывать условно захватывающие файлы и копировать их.

Ответ 3

Я думаю, что лучший подход здесь, вероятно, для использования скриптов сборки. Вы можете иметь весь свой код в одном месте, и, выбирая, какие файлы включать, а какие не включать, вы можете выбрать, какую версию вашего кода нужно компилировать. Обратите внимание, что это может не помочь, если вам нужен более тонкий контроль, чем для каждого файла.

Ответ 4

Храните один "главный" исходный корень, который создается под JDK 5. Добавьте второй параллельный корневой источник, который должен быть создан под JDK 6 или выше. (Не должно быть перекрытия, т.е. Нет классов, присутствующих в обоих.) Используйте интерфейс для определения точки входа между ними и крошечный бит отражения.

Например:

---%<--- main/RandomClass.java
// ...
if (...is JDK 6+...) {
    try {
        JDK6Interface i = (JDK6Interface)
            Class.forName("JDK6Impl").newInstance();
        i.browseDesktop(...);
    } catch (Exception x) {
        // fall back...
    }
}
---%<--- main/JDK6Interface.java
public interface JDK6Interface {
    void browseDesktop(URI uri);
}
---%<--- jdk6/JDK6Impl.java
public class JDK6Impl implements JDK6Interface {
    public void browseDesktop(URI uri) {
        java.awt.Desktop.getDesktop().browse(uri);
    }
}
---%<---

Вы можете настроить их как отдельные проекты в среде IDE с использованием разных JDK и т.д. Дело в том, что основной корень можно скомпилировать независимо, и очень ясно, что вы можете использовать в корне, тогда как если вы попытаетесь скомпилировать разные частей одного корня отдельно, слишком легко случайно "утешить" использование JDK 6 в неправильные файлы.

Вместо того, чтобы использовать Class.forName, как это, вы также можете использовать какую-то систему регистрации услуг - java.util.ServiceLoader(если main может использовать JDK 6, и вам нужна дополнительная поддержка JDK 7!), поиск NetBeans, Spring и т.д. и т.д.

Такую же технику можно использовать для создания поддержки для дополнительной библиотеки, а не для нового JDK.

Ответ 5

Вы, вероятно, можете реорганизовать свой код, чтобы условная компиляция действительно не нужна, просто условная загрузка классов. Что-то вроде этого:

public interface Opener{

public void open(File f);

 public static class Util{
        public Opener getOpener(){
          if(System.getProperty("java.version").beginsWith("1.5")){
           return new Java5Opener();
          }
          try{ 
            return new Java6Opener();
           }catch(Throwable t){
            return new Java5Opener();
           }
        }
 }

}

Это может быть большим количеством усилий в зависимости от того, сколько у вас версий кода, которые вы имеете.

Ответ 6

Не совсем, но есть обходные пути. Видеть http://forums.sun.com/thread.jspa?threadID=154106&messageID=447625

Тем не менее, вы должны придерживаться хотя бы одной версии файла для Java 5 и одного для Java 6 и включать их через сборку или сделать соответствующим образом. Вставляя все это в один большой файл и пытаясь заставить компилятор для 5 игнорировать то, что он не понимает, не является хорошим решением.

НТН

- никки -

Ответ 7

Это заставит всех пуристов Java сжиматься (это весело, хе-хе), но я бы использовал препроцессор C, поместив #ifdefs в свой исходный код. Файл makefile, rakefile или что-то, что контролирует вашу сборку, должно было бы запустить cpp, чтобы временные файлы могли кормить компилятор. Я не знаю, можно ли сделать ant для этого.

В то время как stackoverflow выглядит так, как будто это будет место для всех ответов, вы не можете никому не смотреть на http://www.javaranch.com для мудрости Java. Я предполагаю, что этот вопрос был рассмотрен там, довольно давно.

Ответ 8

Это зависит от того, какие функции Java 6 вы хотите использовать. Для простой вещи, такой как добавление сортировщиков строк в JTables, вы можете протестировать во время выполнения:

private static final double javaVersion =
         Double.parseDouble(System.getProperty("java.version").substring(0, 3));
private static final boolean supportsRowSorter =
         (javaVersion >= 1.6);

//...

if (supportsRowSorter) {
    myTable.setAutoCreateRowSorter(true);
} else {
    // not supported
}

Этот код должен быть скомпилирован с помощью Java 6, но может быть запущен с любой версией (ссылки на новые классы не указаны).

EDIT: вернее, он будет работать с любой версией начиная с версии 1.3 (согласно this страница).

Ответ 9

Вы можете выполнить всю вашу компиляцию исключительно на Java6, а затем использовать System.getProperty( "java.version" ), чтобы условно запустить либо Java5, либо код Java6.

У вас может быть только код Java6 в классе, и класс будет отлично работать на Java5 до тех пор, пока не будет выполнен код кода только для Java6.

Это трюк, который используется для написания апплетов, которые будут работать на древнем MSJVM вплоть до совершенно новых JVM Java Plug-in.

Ответ 10

В Java нет предварительного компилятора. Таким образом, невозможно использовать #ifdef, как в C. Сценарии сборки были бы лучшим способом.

Ответ 11

Вы можете получить условную компиляцию, но не очень хорошо - javac будет игнорировать недостижимый код. Таким образом, если вы правильно структурировали свой код, вы можете заставить компилятор игнорировать части вашего кода. Чтобы использовать это правильно, вам также необходимо передать правильные аргументы в javac, чтобы он не сообщал недостижимый код как ошибки и отказывался компилировать: -)

Ответ 12

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

Итак, я думаю, что точное решение, которое вы искали.

Ответ 13

Простым решением может быть:

  • Поместите расходящиеся классы вне вашего обычного пути к классам.
  • Напишите простой пользовательский загрузчик классов и установите его в качестве основного по умолчанию.
  • Для всех классов, кроме 5/6, cassloader может отложить до своего родителя (обычный системный загрузчик классов)
  • Для 5/6 (которые должны быть единственными, которые не могут быть найдены родителем), он может решить, какой из них использовать через свойство "os.name" или один из ваших собственных.

Ответ 14

Вы можете использовать API отражения. поместите весь ваш код 1.5 в один класс и 1.6 api в другой. В вашем ant script создайте две цели для 1.5, которые не будут компилировать класс 1.6 и один для 1.6, который не будет компилировать класс для 1.5. в вашем коде проверьте свою версию java и загрузите соответствующий класс, используя отражение таким образом, чтобы javac не жаловался на недостающие функции. Вот как я могу скомпилировать приложения MRJ (Mac Runtime для Java) в Windows.