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

StackOverflow https://stackoverflow.com/questions/74171

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Это было полезно?

Решение

Предполагая, что классы имеют схожую функциональность в версиях 1.5 и 1.5.6.0 различия в реализации, вы можете объединить их в один класс.Тогда, не редактируя исходный код для комментирования/раскомментирования, вы можете положиться на оптимизацию, которую всегда делает компилятор.Если выражение if всегда ложно, код оператора 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-файла, но объединит ваш код и устранит любое дублирование кода.Ваш редактор может жаловаться на недостижимый код или что-то еще, но компилятор должен блаженно игнорировать это.

Другие советы

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

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

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

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

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();
           }
        }
 }

}

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

Сохраните один «главный» корень исходного кода, который собирается под 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 Lookup, Spring и т. д.и т. д.

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

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

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

ХТХ

-- Никки --

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

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

Это зависит от того, какие функции 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, но его можно запускать с любой версией (новые классы не упоминаются).

РЕДАКТИРОВАТЬ:точнее будет работать с любой версией начиная с 1.3 (согласно эта страница).

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top