9
votes

Existe-t-il un moyen de déterminer par programme les fonctionnalités du langage Java disponibles sur la plate-forme actuelle?

Je me demandais s'il existe une API Java qui pourrait vous dire si une fonctionnalité de langage particulière (par exemple, l'opérateur "diamant") est disponible sur la plate-forme actuelle.

(En d'autres termes, ce que j'essaye de faire est analogue au "reniflage de navigateur" en JavaScript.)

Ce serait vraiment pratique en méta-programmation (écrire un programme Java qui génère du code source Java.

La meilleure solution que je ' J'ai trouvé jusqu'ici est d'analyser System.getProperty ("java.specification.version") et de vérifier si c'est ≥ la version qui a introduit cette fonctionnalité, mais je ne suis pas sûr à 100% que cette propriété est disponible dans toutes les JVM (ou même si elle est conforme à la même syntaxe dans toutes les JVM). Un autre inconvénient mineur de cette approche est que vous devez faire l'étape supplémentaire de rechercher quelle version de Java a introduit la fonctionnalité de langage qui vous intéresse . Ce n’est pas grave, car cette information est assez facile à rechercher sur Google, mais idéalement, ce serait bien qu’il y ait une API qui puisse facilement fournir l’information, pour exemple:

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

Évidemment, il n'y a pas de package nommé javax.meta , mais je me demandais s'il existait déjà une solution existante pour ce problème qui soit plus propre que d'analyser la propriété "java.specification.version" .


Mise à jour : Je viens de réaliser que Package # getSpecificationVersion () fournit également la même valeur que System.getProperty ("java.specification.version") mais est probablement plus fiable, car les propriétés système sont modifiables. En d'autres termes, la meilleure façon d'obtenir la version de spécification Java est probablement d'appeler Package # getSpecificationVersion () sur n'importe quel package "intégré". Par exemple: String.class.getPackage().getSpecificationVersion()


2 commentaires

Je laisserais simplement l'utilisateur choisir la version Java du programme Java généré et éviter toutes ces choses ennuyeuses ... :)


La seule autre façon que je vois est de compiler par programme un petit programme avec la fonctionnalité prévue et de voir si la compilation échoue ( import java.util. *; Class M {public static void main (String [] a) {Map m = new HashMap <> ();}} ), mais c'est un hack qui rendra également votre code difficilement maintenable et plus lent, donc je me contenterais de décider quoi écrire en fonction de l'installation version du compilateur.


3 Réponses :


4
votes

Cela vous donnera la version de java que le système exécute.

Runtime.Version version = Runtime.version();

Si vous exécutez Java 9 et supérieur, vous pouvez utiliser:

 System.getProperty("java.version")

Documentation Java

Juste une note sur la gestion des versions Java la norme de dénomination a également changé à Java 9.

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

Je n'ai pas de solution pour vous permettre de vérifier des fonctionnalités spécifiques.


1 commentaires

J'apprécie votre réponse, mais j'utilise déjà la propriété java.specification.version (elle renvoie quelque chose comme "1.8" ), ce qui a plus de sens dans ce scénario que java.version (qui renvoie quelque chose comme "1.8.0_151" ), car je n'ai besoin que de la spécification de langue et "1.8" est plus facile à analyser que "1.8.0_151" . Cependant, merci pour les informations sur la nouvelle classe Runtime.Version . Peut-être qu'un jour je passerai à Java 9 :)



3
votes

Une fonctionnalité peut être: JDK 10

  1. Méthode: Optional.orElseThrow()
  2. API: API de création de collections non modifiables
  3. Propriété système: exemple Fore, pour désactiver le suivi de la dernière utilisation JRE
  4. Amélioration du GC (parallèle complet)
  5. Prise en charge de Javadoc: (pour plusieurs feuilles de style)

Cela pourrait aussi être une SUPPRESSION de fonctionnalité: également en Java 10

  1. Suppression de la prise en charge de l'utilisation de l'ancien LookAndFeel
  2. Suppression des méthodes Runtime.getLocalizedInputStream et getLocalizedOutputStream
  3. Et ainsi de suite ..

Il est donc difficile de vérifier ou découvrir par programme si une nouvelle fonctionnalité existe ou si elle a été supprimée SAUF vous sachez ce que vous recherchez, il doit être fourni par Oracle lui-même en tant que documentation, nom de fonctionnalité et description.

Si nous voulons créer une API pour cela, nous devons d'abord obtenir la liste à partir de Documentation Oracle, puis effectuez les vérifications requises pour chaque fonctionnalité pour découvrir la version actuelle ou si elle est prise en charge.

Voici un exemple pour vérifier par programme le compilateur pour une fonctionnalité spécifique.

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

Voir JDK 10 fonctionnalités


1 commentaires

J'aime votre suggestion concernant l'utilisation de l'API de l'outil de compilation. en fait, j'ai juste commencé à utiliser github.com/OpenHFT/Java-Runtime-Compiler dans ma métaprogrammation efforts, mais il ne m'est pas venu à l'esprit qu'il pourrait également être utilisé pour aider à la découverte de fonctionnalités jusqu'à ce que vous le signaliez. Merci! +1



1
votes

Je viens de découvrir une autre solution partielle: javax.lang.model.SourceVersion . Cela ne résout toujours pas entièrement le problème, mais semble être un pas dans la bonne direction.

Donc, au lieu de mon ancienne approche,

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

vous pourriez écrire:

if (SourceVersion.latestSupported().ordinal() >= SourceVersion.RELEASE_7.ordinal()) 
    // do something that requires Java 7

(car SourceVersion a ses constantes enum déclarées par ordre croissant)

Cette classe également fournit les méthodes statiques isKeyword , isName , isIdentifier , qui pourraient être utiles, mais il était déjà assez facile de dériver certaines de ces informations (par exemple Character.isJavaIdentifierPart(cp)).

Sous le capot, SourceVersion s'appuie également sur la lecture du "java.specification.version" propriété système:

if (System.getProperty("java.specification.version").compareTo("1.7") >= 0)
   // do something that requires Java 7

Voilà du code malodorant juste là! par exemple. si quelqu'un appelle System.setProperty ("java.specification.version", "6.9"); alors SourceVersion.latestSupported () renverra RELEASE_5 ;)

Je suis surpris que même Sun n'ait pas fourni un moyen "magique" d'obtenir la version Java directement depuis la VM (comme les différentes opérations "magiques" internes exposées par le sun .misc package)


0 commentaires