有什么方法可以通过编程确定哪些Java语言特性在当前平台上可用吗?

6tdlim6h  于 2023-05-27  发布在  Java
关注(0)|答案(3)|浏览(343)

我想知道是否有一个Java API可以告诉你一个特定的语言特性(例如,“钻石”运算符)在当前平台上可用。
(In换句话说,我尝试做的类似于JavaScript中的“浏览器嗅探”。
这在元编程(编写生成Java源代码的Java程序)中非常方便。
到目前为止,我发现的最佳解决方案是解析System.getProperty("java.specification.version")并检查它是否≥引入此特性的版本,但我不能100%确定此属性在所有JVM中都可用(甚至不能确定它是否符合所有JVM中的相同语法)。这种方法的另一个小麻烦是,您必须采取额外的步骤来查找哪个Java版本引入了您感兴趣的语言特性。没什么大不了的,因为这些信息很容易谷歌,但理想情况下,如果有一个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()
t40tm48m

t40tm48m1#

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

System.getProperty("java.version")

如果你运行的是Java 9及更高版本,你可以用途:

Runtime.Version version = Runtime.version();

Java文档
Java版本命名标准在Java 9中也发生了变化。
Java版本:1.7, 1.8, 9, 10, 11
我没有一个解决方案,让你检查特定的功能。

llmtgqce

llmtgqce2#

特征可以是:JDK 10
1.方法:Optional.orElseThrow()

  1. API:用于创建不可修改集合的API
    1.系统属性:例如,禁用JRE上次使用跟踪
  2. GC增强(完全平行)
  3. Javadoc支持:(适用于多个样式表)
    它也可能是功能的删除:在Java 10中
    1.删除对使用旧LookAndFeel的支持
    1.删除Runtime.getLocalizedInputStreamgetLocalizedOutputStream方法
    1.等等。
    因此,如果存在新功能或已删除新功能,则很难通过编程方式执行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

rjee0c15

rjee0c153#

我刚刚发现了另一个部分解决方案: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

(因为SourceVersionenum常量是按升序声明的)
这个类还提供了静态方法isKeywordisNameisIdentifier,这些方法可能会很有用,但是要获得其中的一些信息已经相当容易了(例如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");,那么SourceVersion.latestSupported()将返回RELEASE_5;)
我感到惊讶的是,即使Sun/Oracle也没有提供一种“内在的”方法来直接从VM获取Java版本(就像sun.misc包公开的各种内在操作一样)

相关问题