是否可以通过编程方式确定当前平台上可用的Java语言功能?

时间:2019-01-10 10:13:01

标签: java

我想知道是否有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()

3 个答案:

答案 0 :(得分:4)

这将为您提供系统正在运行的Java版本。

 System.getProperty("java.version")

如果您正在运行Java 9和更高版本,则可以使用:

Runtime.Version version = Runtime.version();

Java Docs

请注意,Java版本命名标准也在Java 9中进行了更改。

Java版本:1.7, 1.8, 9, 10, 11

我没有解决方案来检查特定功能。

答案 1 :(得分:3)

功能可能是:JDK 10

  1. 方法:Optional.orElseThrow()
  2. API:用于创建不可修改集合的API
  3. 系统属性:例如,禁用JRE上次使用跟踪
  4. GC增强(完全并行)
  5. Javadoc支持:(用于多个样式表)

它也可能是功能的删除:也在Java 10中

  1. 取消对使用旧版LookAndFeel的支持
  2. 删除Runtime.getLocalizedInputStreamgetLocalizedOutputStream方法
  3. 依此类推。

因此,很难通过编程方式checkdiscover(如果有)或已经删除了新功能。除非,您知道自己在寻找什么,就需要由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}}常量以升序声明)

此类还提供了静态方法enumisKeywordisName,这些方法可能很有用,但已经很容易派生某些此类信息(例如{{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()包公开的各种内部“魔术”操作)