Подтвердить что ты не робот

Условная компиляция Java: как предотвратить компиляцию кусков кода?

Для моего проекта требуется Java 1.6 для компиляции и работы. Теперь у меня есть требование заставить его работать с Java 1.5 (с маркетинговой стороны). Я хочу заменить тело метода (возвращаемый тип и аргументы остаются неизменными), чтобы компилировать его с помощью Java 1.5 без ошибок.

Детали: У меня есть класс утилиты OS, который инкапсулирует все специфичные для ОС вещи. Он имеет метод

public static void openFile(java.io.File file) throws java.io.IOException {
  // open the file using java.awt.Desktop
  ...
}

чтобы открыть файлы, например, с помощью двойного щелчка (start команда Windows или open эквивалент команды Mac OS X). Поскольку он не может быть скомпилирован с помощью Java 1.5, я хочу исключить его во время компиляции и заменить другим методом, который вызывает run32dll для Windows или open для Mac OS X с помощью Runtime.exec.

Вопрос: Как я могу это сделать? Могут ли аннотации помочь?

Примечание. Я использую ant, и я могу создать два java файла OS4J5.java и OS4J6.java, которые будут содержать класс OS с нужным кодом для Java 1.5 и 1.6 и скопировать один из них в OS.java перед компиляцией (или уродливым способом - заменить содержимое OS.java условно в зависимости от java-версии), но я не хочу этого делать, если есть другой способ.

Разработано больше: на C я мог бы использовать ifdef, ifndef, в Python компиляции нет, и я мог бы проверить функцию с помощью hasattr или что-то еще, в Common Lisp я мог бы использовать #+feature. Есть ли что-то подобное для Java?

Нашел этот пост, но он не кажется полезным.

Любая помощь приветствуется. кк.

4b9b3361

Ответ 1

Нет никакой поддержки условной компиляции в Java.

Обычный план состоит в том, чтобы скрыть конкретные биты вашего приложения за Interface, а затем определить тип ОС во время выполнения и загрузить реализацию с помощью Class.forName(String).

В вашем случае нет причин, по которым вы не можете скомпилировать оба OS* (и проинформировать все ваше приложение) с помощью Java 1.6 с помощью -source 1.5 -target 1.5, а затем в методе factory для получения классов OS (который теперь станет интерфейсом) обнаруживает, что класс java.awt.Desktop доступен и загружает правильную версию.

Что-то вроде:

 public interface OS {
     void openFile(java.io.File file) throws java.io.IOException;
 }

 public class OSFactory {
     public static OS create(){
         try{
             Class.forName("java.awt.Desktop");
             return new OSJ6();
         }catch(Exception e){
             //fall back
             return new OSJ5();
         }
     }
 }

Ответ 2

Скрытие двух классов реализации за интерфейсом, например, предложенным Гаретом, вероятно, является лучшим способом.

Таким образом, вы можете ввести некоторую условную компиляцию, используя задачу замены в сценариях ant build. Хитрость заключается в использовании комментариев в вашем коде, которые открываются/закрываются текстовой заменой непосредственно перед компиляцией источника, например:

/*{{ Block visible when compiling for Java 6: IFDEF6

public static void openFile(java.io.File file) throws java.io.IOException {
  // open the file using java.awt.Desktop
  ...

/*}} end of Java 6 code. */

/*{{ Block visible when compiling for Java 5: IFDEF5

  // open the file using alternative methods
  ...

/*}} end of Java 5 code. */

теперь в ant, когда вы компилируете для Java 6, замените "IFDEF6" на "*/", указав:

/*{{ Block visible when compiling for Java 6: */

public static void openFile(java.io.File file) throws java.io.IOException {
  // open the file using java.awt.Desktop
  ...

/*}} end of Java 6 code. */

/*{{ Block visible when compiling for Java 5, IFDEF5

public static void openFile(java.io.File file) throws java.io.IOException {
  // open the file using alternative methods
  ...

/*}} end of Java 5 code. */

и при компиляции для Java 5 замените "IFDEF5". Обратите внимание, что вам нужно быть осторожным, чтобы использовать // comments внутри блоков /*{{, /*}}.

Ответ 3

Представленный ниже Ant script дает хороший и чистый трюк.

ссылка: https://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

в примере,

//[ifdef]
public byte[] getBytes(String parameterName)
        throws SQLException {
    ...
}
//[enddef]

с Ant script

        <filterset begintoken="//[" endtoken="]">
            <filter token="ifdef" value="${ifdef.token}"/>
            <filter token="enddef" value="${enddef.token}"/>
        </filterset>

перейдите по ссылке выше для более подробной информации.

Ответ 4

Вы можете совершать вызовы с помощью отражения и компилировать код с помощью Java 5.

например.

Class clazz = Class.forName("java.package.ClassNotFoundInJavav5");
Method method = clazz.getMethod("methodNotFoundInJava5", Class1.class);
method.invoke(args1);

Вы можете поймать любые исключения и вернуться к чему-то, что работает на Java 5.

Ответ 5

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

http://www.javapractices.com/topic/TopicAction.do?Id=64

Цитирование значения:

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

  • определяет статическое конечное логическое значение как неединичный член некоторого класса
  • код места, который должен быть условно скомпилирован в блоке if, который оценивает логическое
  • установить значение boolean в false, чтобы заставить компилятор игнорировать блок if; в противном случае сохраните его значение как true

Конечно, это позволяет нам "компилировать" куски кода внутри любого метода. Чтобы удалить члены класса, методы или даже целые классы (возможно, оставив только заглушку), вам все равно нужен предварительный процессор.

Ответ 6

В java 9 можно создавать файлы с несколькими релизами. По сути, это означает, что вы делаете несколько версий одного и того же java файла.

Когда вы их компилируете, вы компилируете каждую версию java файла с требуемой версией jdk. Затем вам нужно упаковать их в структуру, которая выглядит так:

+ com
  + mypackage
    + Main.class
    + Utils.class
+ META-INF
  + versions
    + 9
      + com
        + mypackage
          + Utils.class

В приведенном выше примере основная часть кода компилируется в java 8, но для java 9 существует дополнительная (но другая) версия класса Utils.

Когда вы запускаете этот код JVM java 8, он даже не проверяет классы в папке META-INF. Но в java 9 он будет, и найдет и использует более новую версию класса.

Ответ 7

если вы не хотите использовать условно включенные кодовые блоки в своем приложении, тогда препроцессор будет только способом, вы можете взглянуть на java-comment-preprocessor, который может использоваться для проектов maven и ant
P.S.
также я сделал пример использования предварительной обработки с Maven для создания JAR-238 с несколькими версиями JAR без дублирования источников

Ответ 8

Существует новый препроцессор для Java из структуры коллектора. Это плагин javac, который означает, что он напрямую интегрирован с компилятором Java - никаких шагов сборки, никаких целей генерации кода и т.д. Для управления.

enter image description here

Ответ 9

Java Primitive Specializations Generator поддерживает условную компиляцию:

   /* if Windows compilingFor */
   start();
   /* elif Mac compilingFor */
   open();
   /* endif */

Этот инструмент имеет плагины Maven и Gradle.