Обходной путь для ошибки порядка компиляции javac в maven
Я встречаюсь с ошибкой в компиляторе Java, где порядок файлов, отправленных для компиляции, может привести к тому, что код не будет компилироваться. Я развернул код, чтобы изолировать наименьший объем кода, который мог бы воспроизвести проблему, в результате получилось три исходных файла (по 1 классу).
public interface ActionSpec {
public abstract int run(String param);
}
public enum Actions implements ActionSpec {
SKIP {
public int run(String d) {
return 0;
}
};
}
public class Program {
public static void main(String[] args) {
Actions.SKIP.run("hello");
}
}
Проблема воспроизводима с помощью аргументов javac в определенном порядке. Короче говоря, чтобы добиться успеха, класс Actions всегда должен быть скомпилирован перед классом программы, который его использует, иначе javac просто не справится с ним разумным способом:
# this case fails
echo "Trying order: javac Program.java Actions.java ActionSpec.java"
rm *class
javac -verbose Program.java Actions.java ActionSpec.java
# this case fails
#rm *class
#javac Program.java Actions.java ActionSpec.java
# this case fails
#rm *class
#javac ActionSpec.java Program.java Actions.java
# this case succeeds
#rm *class
#javac ActionSpec.java Actions.java Program.java
# this case succeeds
#rm *class
#javac Actions.java ActionSpec.java Program.java
# this case succeeds
#rm *class
#javac Actions.java Program.java ActionSpec.java
Ошибка компиляции, когда она встречается, всегда одна и та же: метод run в экземплярах enum Actions не может быть найден, хотя все они реализуют интерфейс, который имеет этот метод запуска.
Program.java:6: cannot find symbol
symbol : method run(java.lang.String)
location: class problem.Actions
Actions.SKIP.run("hello");
Ошибка, связанная с об этом сообщается на сайте Oracle.
Я использую javac 1.6.0_29, на mac os x 10.7.2 x86_64, но также воспроизвел его на Linux.
Эта проблема стала очевидной, поскольку я использую Maven для сборки и, похоже, не контролирую порядок компиляции. Поэтому я ищу обходное решение, чтобы либо заставить maven скомпилировать файлы в таком порядке, чтобы избежать ошибки компилятора, либо возиться с флагами компилятора (или что-то вроде этого), чтобы избежать этого. Проблема возникает на рабочих станциях и в непрерывных средах интеграции, поэтому она должна работать по всем направлениям. Любые предложения?
EDIT: просто попробовал следующее обходное решение, которое, несмотря на то, что просто присваивает рассматриваемое перечисление переменной с типом интерфейса, который она реализует, неожиданно приводит к исчезновению ошибки.
public class Program {
public static void main(String[] args) {
ActionSpec a = Actions.SKIP;
a.run("hello");
}
}
Все еще интересуются мнениями других.
Ответы
Ответ 1
Я играл, и обнаружил, что добавление простого актера:
public static void main(String[] args) {
((ActionSpec)Actions.SKIP).run("hello");
}
решает эту проблему. Передача этого enum в качестве параметра метода как интерфейса также приведет к трюку
Ответ 2
Это ошибка, о которой сообщается в http://bugs.sun.com/view_bug.do?bug_id=6724345
Предлагаемое обходное решение должно работать, если вы все еще используете компилятор Java 6. Исправлена ошибка в Java 7.
Ответ 3
Вот как бы я это сделал:
- Переместите интерфейс
ActionSpec
в другой проект Maven. Обычно у нас есть интерфейсы и общие классы домена в их собственном проекте, например. foo-service-specs
.
- Сохранять другие классы в проекте реализации, например.
foo-service-impl
.
- Включить проект
foo-service-specs
в качестве зависимости в foo-service-impl
.
Сделав это, вы можете убедиться, что порядок компиляции работает, и он также должен работать для непрерывной интеграции.
Ответ 4
Попробуйте выполнить несколько исполнений плагина компилятора. Используя default-compile
, поскольку первый идентификатор выполнения добавляет новую конфигурацию к исполнению компилятора Maven по умолчанию. Используйте элементы конфигурации <includes/>
в выполнении по умолчанию, чтобы скомпилировать перечисления в первую очередь. Для второго исполнения вы должны использовать комбинацию <includes>
и <excludes>
, где <includes>
будет всем вашим кодом, и исключение будет состоять из уже переписанных перечислений.
Я думаю, что это будет работать для вашей примерной программы, но я не тестировал ее. Я тестировал нечто подобное раньше с Maven 3, и он работал нормально.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
<executions>
<execution>
<id>default-compile</id>
<goals><goal>compile</goal></goals>
<configuration>
<includes>
<include>**/Actions.*</include>
</includes>
</configuration>
</execution>
<execution>
<id>second</id>
<goals><goal>compile</goal></goals>
<configuration>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/Actions.*</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
Ответ 5
У нас была та же проблема. Множество исполнений плагина maven compiler
работало для нас...