有没有办法给java编译器一些可以运行的java代码访问的变量?
在C / C ++中,我可以给出编译-DKEY=VALUE
,这将导致预处理器的KEY #define
等于VALUE。然后我可以在编译时检查这个值,以影响正在编译的代码
我找到java
的-D,但是这些值会在java
中提供System.getProperty()
命令行。我希望在编译时给出一个参数,而不是调用时间。
答案 0 :(得分:11)
答案 1 :(得分:8)
使用java注释,可以动态生成其他代码,可以在命令行上配置。它允许生成更多的源代码,配置文件,xml文件,...主要限制是你只允许(重新)生成新的源文件,你不能修改现有的源文件。
下面是一个简短的教程,介绍如何允许javac命令指定在Java代码中可见的参数。这有用吗? IE浏览器。你可以指定一个布尔选项来禁用代码的某些部分,我很确定这些代码部分可以使用proguard之类的工具删除 - 甚至可以通过javac进行优化。其他用途是指定新版本号。这些用例主要是使用c ++ marcros。
所以,你需要:
在编译Main类之前,您还必须编译处理器文件。这当然仅在修改处理器类时完成。所有这三个文件都在底部。现在编译看起来如下(我在Windows上):
编译处理器:
javac .\com\example\ConfigWritterAnnotationProcessor.java
然后Main.java带有处理器的附加参数:
javac -processor com.example.ConfigWritterAnnotationProcessor -AtextToPrint="Hello World!" -AenablePrint=true ./com/example/Main.java
这就是全部,现在您可以运行Main.class,它将使用在编译期间使用上述参数设置生成的Options类。它看起来如下:
package com.example;
public class Options {
public static final String textToPrint = "Hello World!";
public static final boolean enablePrint = true;
}
ProcessorStarterAnnotation.java
package com.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
public @interface ProcessorStarterAnnotation {
}
Main.java
package com.example;
@ProcessorStarterAnnotation
public class Main {
public static void main(String[] args) {
if ( com.example.Options.enablePrint ) {
System.out.println(com.example.Options.textToPrint);
}
else {
System.out.println("Print disabled");
}
}
}
ConfigWritterAnnotationProcessor.java
package com.example;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Map;
import java.util.Set;
@SupportedAnnotationTypes("com.example.ProcessorStarterAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedOptions({"textToPrint", "enablePrint"})
public class ConfigWritterAnnotationProcessor extends AbstractProcessor {
private Map<String,String> options;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
options = processingEnv.getOptions();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment currentRound) {
if (!currentRound.processingOver()) {
// This for-s are because processor is also run on newly created Options class.
for (TypeElement te : annotations) {
for (Element e : currentRound.getElementsAnnotatedWith(te)) {
try {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Creating com.example.Options");
JavaFileObject javaFile = processingEnv.getFiler().createSourceFile("com.example.Options");
Writer w = javaFile.openWriter();
try {
PrintWriter pw = new PrintWriter(w);
pw.println("package com.example;");
pw.println("public class Options {");
pw.println(" public static final String textToPrint = \"" + options.get("textToPrint") + "\";");
pw.println(" public static final boolean enablePrint = " + options.get("enablePrint") + ";");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}
}
}
return false;
}
}
答案 2 :(得分:2)
Java中没有这样的东西。编译时间常量必须在源代码中声明,据我所知,没有预处理器。
BTW,您可以使用java
命令行(-D args)的标志来在运行时初始化java常量,这将模仿您正在寻找的内容。
例如:
class Foo {
private static final String BAR;
static {
String foobar = System.getProperty("foo.bar");
if(foobar != null && foobar.length()>0) {
BAR = foobar;
} else {
BAR = "somedefaultvalue";
}
}
}
使用java Xxx -Dfoo.bar=foobar
答案 3 :(得分:1)
由于Java中没有预处理的概念,因此解决方案是设计自己的。
可以考虑使用标准的C预处理器或定制的预处理器并编译预处理的输出,但这样做的缺点是重复文件,因此项目将变得更加复杂,并且来自开发环境的支持会降级(比如跳转到语法错误的能力)。
我的建议是通过注释使用注释来引导自定义预处理器,并在编译之前让它进行替换。
例如,
public static void main(String[] args) {
int nDisks = 3;
doTowers(nDisks, 'A', 'B', 'C');
}
会变成
public static void main(String[] args) {
int nDisks = /*@NDISKS*/ 3 /**/;
doTowers(nDisks, 'A', 'B', 'C');
}
然后您的预处理器将有一个定义文件,如
NDISKS 5
转换
中的代码 public static void main(String[] args) {
int nDisks = /*@NDISKS*/ 5 /**/;
doTowers(nDisks, 'A', 'B', 'C');
}
同样,您可以使用
模拟条件代码编译 doTowers(topN - 1, from, to, inter);
/*!PRINT*/
System.out.println("Disk "
+ topN + " from " + from + " to " + to);
/**/
doTowers(topN - 1, inter, from, to);
可以由预处理器(具有类似PRINT OFF
的定义)转换为
doTowers(topN - 1, from, to, inter);
/*!PRINT
System.out.println("Disk "
+ topN + " from " + from + " to " + to);
*/
doTowers(topN - 1, inter, from, to);
您可以使用其他语法,但主要想法是
答案 4 :(得分:0)
以这种方式拥有它将违背语言的设计。 KEY
做的是它在C / C ++预处理器中实际上用VALUE
替换KEY
。
Java没有预处理器,因此机制不可用。如果你想要“等同”的东西,你必须质疑你的意思。通过不预处理源,它不会真正等效。
另一方面,如果您希望将符号VALUE
的值设置为值KEY
,则会遇到需要声明符号的问题{ {1}}无论如何要确定它的类型。在这种情况下,它只是另一个具有暗示约束的常量/变量。
这意味着即使使用这样的功能,它也不会实际改变生成的代码,你几乎不会比定义值的启动时间更好。这就是通过java
提供参数的原因。