我想知道是否有Java API可以告诉您当前平台上是否有特定语言功能(例如“钻石”运算符)。
(换句话说,我想要做的事情类似于JavaScript中的“浏览器嗅探”。)
这在元编程(编写生成Java源代码的Java程序)中非常方便。
到目前为止,我找到的最好的解决方案是解析System.getProperty("java.specification.version")
并检查它是否≥引入此功能的版本,但是我不确定100%确保此属性在所有JVM中都可用(或即使它是否在所有JVM中都遵循相同的语法)。这种方法的另一个小麻烦是您必须采取额外的步骤来查找哪个Java版本引入了您感兴趣的语言功能。这没什么大不了的,因为该信息非常容易在Google上搜索到,但是理想情况下会如果有一个可以随时提供信息的API,那就太好了,例如:
code.append("Map<Integer, String> map = ");
if (javax.meta.JavaVersion.getCurrentVersion().supportsDiamond()) {
code.append("new Map<>();");
} else {
code.append("new Map<Integer, String>();");
}
显然没有名为javax.meta
的程序包,但我想知道是否可能已经有解决此问题的现有解决方案,比解析"java.specification.version"
属性更干净。
更新:我刚刚意识到Package#getSpecificationVersion()
也提供与System.getProperty("java.specification.version")
相同的值,但可能更可靠,因为系统属性是可变的。换句话说,获得Java规范版本的最佳方法可能是在任何“内置”程序包上调用Package#getSpecificationVersion()
。例如:String.class.getPackage().getSpecificationVersion()
答案 0 :(得分:4)
这将为您提供系统正在运行的Java版本。
System.getProperty("java.version")
如果您正在运行Java 9和更高版本,则可以使用:
Runtime.Version version = Runtime.version();
请注意,Java版本命名标准也在Java 9中进行了更改。
Java版本:1.7, 1.8, 9, 10, 11
我没有解决方案来检查特定功能。
答案 1 :(得分:3)
功能可能是:JDK 10
Optional.orElseThrow()
它也可能是功能的删除:也在Java 10中
Runtime.getLocalizedInputStream
和getLocalizedOutputStream
方法因此,很难通过编程方式check
或discover
(如果有)或已经删除了新功能。除非,您知道自己在寻找什么,就需要由Oracle本身作为文档,功能名称和描述提供。
如果要为此创建API,我们必须首先从Oracle文档中获取列表,然后对每个功能进行必要的检查以发现当前版本或受支持。
以下是example,用于以编程方式检查编译器的特定功能。
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject.Kind;
public class CompileSourceInMemory {
public static void main(String args[]) throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StringWriter writer = new StringWriter();
PrintWriter out = new PrintWriter(writer);
out.println("public class HelloWorld {");
out.println(" public static void main(String args[]) {");
out.println(" System.out.println(\"This is in another java file\");");
out.println(" }");
out.println("}");
out.close();
JavaFileObject file = new JavaSourceFromString("HelloWorld", writer.toString());
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits);
boolean success = task.call();
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
System.out.println(diagnostic.getCode());
System.out.println(diagnostic.getKind());
System.out.println(diagnostic.getPosition());
System.out.println(diagnostic.getStartPosition());
System.out.println(diagnostic.getEndPosition());
System.out.println(diagnostic.getSource());
System.out.println(diagnostic.getMessage(null));
}
System.out.println("Success: " + success);
if (success) {
try {
Class.forName("HelloWorld").getDeclaredMethod("main", new Class[] { String[].class })
.invoke(null, new Object[] { null });
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e);
} catch (NoSuchMethodException e) {
System.err.println("No such method: " + e);
} catch (IllegalAccessException e) {
System.err.println("Illegal access: " + e);
} catch (InvocationTargetException e) {
System.err.println("Invocation target: " + e);
}
}
}
}
class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
请参见JDK 10 features
答案 2 :(得分:1)
我刚刚发现了另一个部分解决方案:javax.lang.model.SourceVersion。它仍然不能完全解决问题,但似乎是朝着正确方向迈出的一步。
所以不是我以前的方法,
if (System.getProperty("java.specification.version").compareTo("1.7") >= 0)
// do something that requires Java 7
您可以写:
if (SourceVersion.latestSupported().ordinal() >= SourceVersion.RELEASE_7.ordinal())
// do something that requires Java 7
(因为SourceVersion
的{{1}}常量以升序声明)
此类还提供了静态方法enum
,isKeyword
,isName
,这些方法可能很有用,但已经很容易派生某些此类信息(例如{{1 }}。
在后台,isIdentifier
还依赖于读取Character.isJavaIdentifierPart(cp)
系统属性:
SourceVersion
那是一些臭代码!例如如果有人致电"java.specification.version"
,那么private static SourceVersion getLatestSupported() {
try {
String specVersion = System.getProperty("java.specification.version");
if ("1.8".equals(specVersion))
return RELEASE_8;
else if("1.7".equals(specVersion))
return RELEASE_7;
else if("1.6".equals(specVersion))
return RELEASE_6;
} catch (SecurityException se) {}
return RELEASE_5;
}
将返回System.setProperty("java.specification.version", "6.9");
;)
令我惊讶的是,甚至Sun也没有提供直接从VM获得Java版本的“魔术”方式(例如SourceVersion.latestSupported()
包公开的各种内部“魔术”操作)