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()
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.
A feature could be: JDK 10
Optional.orElseThrow()
It Could also be a REMOVAL of feature : also in Java 10
Runtime.getLocalizedInputStream
and getLocalizedOutputStream
MethodsSo 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With