Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any way to programmatically determine which Java language features are available on current platform?

Tags:

java

I was wondering if there is a Java API that could tell you whether a particular language feature (e.g. "diamond" operator) is available on the current platform.

(In other words, what I'm trying to do is analogous to "browser sniffing" in JavaScript.)

This would be really handy in meta-programming (writing a Java program that generates Java source code.

The best solution I've found so far is to parse System.getProperty("java.specification.version") and check whether it's ≥ the version that introduced this feature, but I'm not 100% sure that this property is available in all JVMs (or even whether it conforms to the same syntax in all JVMs). Another minor annoyance with this approach is that you have to take the extra step of looking up which version of Java introduced the language feature you're interested in. Not a big deal, since that info is pretty easy to google, but ideally it would be nice if there was an API that could readily provide the info, for example:

code.append("Map<Integer, String> map = ");
if (javax.meta.JavaVersion.getCurrentVersion().supportsDiamond()) {
    code.append("new Map<>();");
} else {
    code.append("new Map<Integer, String>();");
}

Obviously there's no package named javax.meta, but I was wondering if there might already be an existing solution for this problem that's cleaner than parsing the "java.specification.version" property.


Update: I just realized that Package#getSpecificationVersion() also provides the same value as System.getProperty("java.specification.version") but is probably more reliable, because System properties are mutable. In other words, the best way to get the Java spec version is probably to call Package#getSpecificationVersion() on any "built-in" package. For example: String.class.getPackage().getSpecificationVersion()

like image 707
typeracer Avatar asked Jan 10 '19 10:01

typeracer


2 Answers

This will give you the version of java that the system is running.

 System.getProperty("java.version")

If you're running Java 9 and greater you can use:

Runtime.Version version = Runtime.version();

Java Docs

Just a note the Java versioning naming standard changed at Java 9 as well.

Java Version: 1.7, 1.8, 9, 10, 11

I don't have a solution for you to check for specific features.

like image 160
Lance Avatar answered Oct 13 '22 04:10

Lance


A feature could be: JDK 10

  1. Method : Optional.orElseThrow()
  2. API : API for Creating Unmodifiable Collections
  3. System Property : Fore example, to Disable JRE Last Usage Tracking
  4. GC enhancement (Full parallel)
  5. Javadoc Support : (For Multiple Stylesheets)

It Could also be a REMOVAL of feature : also in Java 10

  1. Removal of Support for Using Old LookAndFeel
  2. Removal of Runtime.getLocalizedInputStream and getLocalizedOutputStream Methods
  3. And so on..

So it is hard to check or discover programmatcally if a new feature exist or of it has been removed UNLESS you know what you are looking for, it needs to to be provided by Oracle itself as a documentation, feature name and description.

If we are going to create and API for that, we must get the list first from Oracle docs, and then do required checks for each feature to discover the current version or if it is supported.

Following is an example to programmaticaly check the compiler for a specific functionality.

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;
  }
}

See JDK 10 features

like image 34
TiyebM Avatar answered Oct 13 '22 05:10

TiyebM