Script Java! = JavaScript. Cinq javas dans une classe. Nous écrivons pour nous souvenir pour toujours


Cette semaine, le groupe JUG.ru devrait publier une annonce. Jusqu'à ce que je dis quoi. La participation à des projets secrets éveille la créativité, alors voici une autre vidéo de nuit sur Java.

Des nouvelles incroyables: maintenant ce n'est plus une heure et demie, mais environ 20 minutes, et il y a même quelque chose à regarder. Un peu moins que complètement, il s'agit d'un screencast. Ceux qui ne supportent pas ces déchets vidéo et aiment consommer le décryptage de texte, ont dû couper beaucoup de texte après la coupe. Welkam et que Java soit avec vous.

Bientôt 12 java seront publiés, et beaucoup sont encore assis sur Semerochka et croient que s'ils se mettent à jour, ils ne verront rien de particulièrement nouveau ou intéressant. Dans ce très court numéro, nous allons apprendre à transformer la vie de nos collègues en enfer à l'aide de Java scripté, et à quelques endroits, je vis de nouveaux gadgets. Eh bien, les obscurantistes, bien sûr, ne seront pas sauvés du progrès.

Dites-moi, pourquoi mettez-vous à jour votre idée? De nouvelles fonctionnalités y apparaissent constamment, de nouveaux raccourcis, multipliant par dix votre productivité en tant que développeur. Mais vous ne les connaissez pas. Lisez-vous vraiment ces nouvelles? Si vous êtes un utilisateur moyen, alors probablement pas. En principe, vous n'en avez rien à foutre la plupart du temps. Vous mettez à jour l'idée simplement parce que ... vous le pouvez. Parce que les skins sont magnifiques, le thème sombre, la barre tactile du MacBook. Mais une telle explication ne manque pas devant les autorités comme la réponse «pourquoi acheter l'idée».

Au lieu de cela, nous pouvons dire que le 29 octobre, plus récemment, JetBrains a épinglé le support des lignes brutes dans la version bêta d' Idea . L'idée est en version bêta, les lignes sont dans l'aperçu, mais ce n'est plus important pour personne. Si vous êtes si têtu que vous mettez 12 java, alors vous avez des problèmes plus graves. Je le sais par moi-même.

Voyons à quoi cela ressemble dans la pratique. Pour ce faire, essayez de résoudre une sorte de problème de démonstration. Très souvent, il y a le problème de l'écriture de quelque chose. Par exemple, dans les jeux informatiques, ce sont des quêtes, dans jenkins, ce sont des scripts de construction, etc. Habituellement, Python ou Grooves sont utilisés pour cela, et prenons et utilisons Java nu! Pourquoi, pourquoi? Parce que nous pouvons, en trois lignes, et même sans hacks. Cela ressemble à une excellente idée :-)

Où regarder


Tout est sur le github .

Portée


Imaginez que nous ayons un fichier comme celui-ci:

package io.javawatch; public class HelloHabrPrototype { public String get() { return "Hello Habr!"; } }; 

Nous voulons qu'il soit exécuté non pas lors de la compilation de l'application entière, mais en tant que chaîne - après le lancement. Comme un script, c'est.

Manuellement


Vous devez d'abord tout dépasser en une chaîne.

 private static final String SOURCE = "\n" + " package io.javawatch;\n" + " public class HelloHabr {\n" + " public String get() {\n" + " return \"Hello Habr!\";\n" + " }\n" + " };"; public static final String CNAME = "io.javawatch.HelloHabr"; private static String CODE_CACHE_DIR = "C:/temp/codeCache"; 

Tous ces "\ n" atouts et tirets semblent extrêmement misérables.

Auparavant, tout ce que nous pouvions faire était de mettre le code dans un fichier et de le lire. C'est probablement une bonne solution, mais elle n'est pas toujours adaptée. Par exemple, vous êtes conférencier lors d'une conférence et montrez le code des diapositives. Même la construction ci-dessus est bien meilleure qu'une simple référence non étayée que vous avez un code quelque part qui fait quelque chose là-bas. Le changement de diapositives consomme du temps et l'attention du public. Eh bien et ainsi de suite. En bref, les cas, lorsque vous avez besoin du code exactement en ligne, vous pouvez trouver.

Nous avons maintenant la possibilité de nous débarrasser des ordures à l'aide de chaînes brutes. Nous nous levons avec le curseur sur le code, appuyez sur Alt + Entrée (ou tout ce qui s'exécute sur votre système d'exploitation Quck Fix dans l'idée). Sélectionnez "convertir en chaîne littérale brute" et obtenez ce nyashka:

 private static final String SOURCE = ` package io.javawatch; public class HelloHabr { public String get() { return "Hello Habr!"; } };`; public static final String CNAME = "io.javawatch.HelloHabr"; private static String CODE_CACHE_DIR = "C:/temp/codeCache"; 

À mon avis, pour cette fonctionnalité, il vaut déjà la peine d'exécuter JDK 12.

Soit dit en passant, pour que la fonctionnalité fonctionne, vous devrez faire plusieurs choses:

  • Téléchargez JDK 12 et enregistrez-vous dans les paramètres du projet
  • Dans les paramètres globaux de javac, définissez l'indicateur --release , la version 12 du bytecode, des indicateurs supplémentaires --enable-preview -Xlint:preview
  • Maintenant, dans toute configuration d'exécution / débogage, vous devez ajouter l'indicateur VM --enable-preview

Si vous ne comprenez pas, comment cela se fait, consultez mon screencast de la rubrique post, tout est assez clair là-bas.

Maintenant, pour commencer, vous devez effectuer trois étapes simples: transformer la chaîne en une représentation interne pratique de la source, la compiler et exécuter:

  public static void main(String[] args) throws Exception { /* 1 */ RuntimeSource file = RuntimeSource.create(); //SimpleJavaFileObject /* 2 */ compile(Collections.singletonList(file)); /* 3 */ String result = run(); /* 4 */ /* ??? */ /* 5 */ /* PROFIT! */ System.out.println(result); } 

Il existe une classe SimpleJavaFileObject pour la «représentation pratique», mais elle a une fonctionnalité intéressante. C'est cruellement abstrait. Autrement dit, sa méthode clé, que la source compilée doit renvoyer, lance toujours une exécution dans l'espoir que nous la sous-classerons:

  /** * This implementation always throws {@linkplain * UnsupportedOperationException}. Subclasses can change this * behavior as long as the contract of {@link FileObject} is * obeyed. */ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { throw new UnsupportedOperationException(); } 

Vous devez donc écrire une partie de votre héritier. Veuillez noter que le constructeur SimpleJavaFileObject origine veut un URI pour la classe compilée, mais où l'obtenons-nous? Par conséquent, je suggère de simplement le coller de la manière la plus évidente, comme ici dans la fonction buildURI :

  public static class RuntimeSource extends SimpleJavaFileObject { private String contents = null; @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return contents; } private static RuntimeSource create() throws Exception { return new RuntimeSource(CNAME, SOURCE); } public RuntimeSource(String className, String contents) throws Exception { super(buildURI(className), Kind.SOURCE); this.contents = contents; } public static URI buildURI(String className) { // io.javawatch.HelloHabr -> // string:///io/javawatch/HelloHabr.java URI uri = URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension); System.out.println(uri); return uri; } 

Passons maintenant à la compilation:

  public static void compile(List<RuntimeSource> files) throws IOException { File ccDir = new File(CODE_CACHE_DIR); if (ccDir.exists()) { FileUtils.deleteDirectory(ccDir); FileUtils.forceMkdir(ccDir); } JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); Logger c = new Logger(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(c, Locale.ENGLISH, null); Iterable options = Arrays.asList("-d", CODE_CACHE_DIR, "--release=12", "--enable-preview", "-Xlint:preview"); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, c, options, null, files); if (task.call()) { System.out.println("compilation ok"); } } 

Remarque: nous passons quatre drapeaux pour l'assemblage, dont trois sont chargés de prescrire exactement les mêmes options que nous avons faites avec la souris dans les paramètres javac de l'Idea.

Et enfin, exécutez notre classe temporaire:

  public static String run() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, MalformedURLException { //   File ccDir = new File(CODE_CACHE_DIR); ClassLoader loader = new URLClassLoader(new URL[]{ccDir.toURL()}); var clss = loader.loadClass("io.javawatch.HelloHabr"); // Java 10 //    Object instance = clss.getConstructor().newInstance(); // Java 9 // Object instance = clss.newInstance(); Method thisMethod = clss.getDeclaredMethod("get"); Object result = thisMethod.invoke(instance); return (String) result; } 

Veuillez noter que var clss = loader.loadClass plus pratique pour écrire que Class<?> clss = loader.loadClass , et n'introduit pas de nouveaux vorings. Le mot-clé var est apparu dans le Top Ten.

Notez également que clss.newInstance() proposé pour être coupé à partir de Neuf. Il avale les exceptions, ce qui est mauvais. The Nine suggère d'abord d'appeler getConstructor , qui est paramétré et lève les exceptions correctes.

Veuillez également noter que j'ai appelé la variable le mot class , mais Java n'a pas juré. Devoirs: vous pouvez le faire de plusieurs manières, vous devez comprendre laquelle est la plus intéressante et pourquoi.

Eh bien, en général, c'est tout. Ça marche.

Automatisation


Ici, le critique s'exclamera, tout est en noir ici, car il y a des bibliothèques dans le monde Java qui compilent tout pour vous en une seule ligne.

OK, jetons un coup d'œil. Voici le code sur la bibliothèque JOOR, située en haut de Google:

 package io.javawatch; import org.joor.Reflect; import java.util.function.Supplier; public class Automatic { public static void main(String[] args) { Supplier<String> supplier = Reflect.compile( "io.javawatch.HelloHabr", ` package io.javawatch; public class HelloHabr implements java.util.function.Supplier<String> { public String get() { return "Hello Habr!"; } };` ).create().get(); System.out.println(supplier.get()); } } 

Comme si tout allait bien. En effet, sur une seule ligne, sauf que je devais faire glisser le saplayer.

Mais il y a une nuance. Essayez de retourner "Bonjour Habr!" comme littéral de chaîne brute:

 public String get() { return ``Hello Habr!``; } 

Tout se bloquera instantanément avec l'erreur "(utilisez --able-preview pour activer les littéraux de chaîne bruts)". Mais l'avons-nous déjà allumé? Oui, au diable avec deux. Nous l'avons inclus dans l'Idée, et JOOR le construit avec un compilateur système! Jetons un coup d'œil à ce qu'il y a à l'intérieur:

 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); ompiler.getTask(out, fileManager, null, null, null, files).call(); 

Et qu'en est-il de nous quand nous appelions nous-mêmes le même JavaCompiler?

  Iterable options = Arrays.asList("-d", CODE_CACHE_DIR, "--release=12", "--enable-preview", "-Xlint:preview"); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, c, options, null, files); 

Et dans JOOR, il y a juste un null nul au lieu d'options. Ils ne peuvent même pas être passés à l'intérieur!

C'est probablement une bonne idée de leur envoyer une telle pull request à Github dans JOOR. Si je ne vais pas le faire moi-même, tu le fais, hein?

Et la morale est simple, ayez peur des cadeaux open source. Parfois, il est plus facile d'écrire un petit mur de texte, mais contrôlez-le.

Manière la plus simple


Il existe un moyen de ne pas écrire un mur de texte et de ne pas utiliser de bibliothèques suspectes. À partir de Java 9, nous avons un shell interactif (similaire à celui de Python, Ruby et d'autres langages de script) qui peut exécuter des commandes Java. Et bien sûr, il est écrit en Java lui-même et est disponible en tant que classe Java. Vous pouvez en créer une instance et simplement effectuer directement l'affectation nécessaire:

 public class Shell { public static void main(String[] args) { var jShell = JShell.create(); //Java 9 jShell.eval("String result;"); jShell.eval(`result = "Hello Habr!";`); //Java 12 var result = jShell.variables() //Streams: Java 8, var: Java 10 .filter((@Nullable var v) -> v.name().equals("result")) //var+lambda: Java 11 .findAny() .get(); System.out.println(jShell.varValue(result)); } } 

Les flux sont apparus dans Java 8, et maintenant ils sont utilisés partout, et vous n'avez même plus besoin d'appeler stream() vous-même, d'autres l'appelleront pour vous. Dans ce cas, l'appel de variables() renvoie exactement le flux, pas l'ArrayList, comme l'aurait fait quelqu'un d'une enfance difficile de sept ans. Le résultat du flux peut être immédiatement versé dans var .

Notez que vous pouvez maintenant écrire var également dans les paramètres lambda. Cette fonctionnalité est présente depuis Java 11.

En général, il s'agit d'une classe très importante. Il utilise un tas de fonctionnalités de différentes générations de Java, et tout cela a un aspect holistique et harmonieux. Je suis sûr que de telles choses seront utilisées par tout le monde et partout, donc le code de Java 12 sera directement différent visuellement de ce que nous avions en Java 7.

Pour résumer


Nous avons examiné quelques fonctionnalités:

  • 8: Stream (pour ceux dans le réservoir)
  • 9: JShell et nouvelles méthodes obsolètes
  • 10: mot clé var
  • 11: Développement var pour les paramètres lambda
  • 12: Littéraux à cordes brutes et leur support dans IntelliJ IDEA

Un petit avertissement pour les débutants. Si vous faites constamment cela dans des applications réelles sans signification particulière, vos collègues deviendront fous au début, essayant de comprendre ce que vous avez écrit, puis ils pourront vous battre très douloureusement. Et je ne parle pas de nouvelles fonctionnalités Java maintenant, mais de la compilation en runtime, bien sûr.

Si vous souhaitez en savoir plus sur Java en tant que langage, vous devriez, par exemple, consulter les rapports de Tagir Valeev. Vous le connaissez en tant que mec du haut du hub Java à Habré, lany . Au Joker, il a parlé d' Amber , et au JPoint - ses célèbres puzzles , et ainsi de suite. Là, et sur le langage Java, et sur IntelliJ IDEA, tout y est. Tout cela peut être trouvé sur YouTube . En fait, par jalousie de Tagir, qui peut dire quelque chose sur la langue elle-même, mais je ne le fais pas, et ce message s'est avéré. Il y aura également de nouveaux jokers et JPoint, mais nous en discuterons plus tard.

J'ai rempli mon devoir moral, un devoir envers Java. De mon côté, les balles ont volé. Et vous là-bas, sur les sept, qui n'avez pas le douzième Java, restez ici, vous tout le meilleur, la bonne humeur et la santé. Je vous remercie

Source: https://habr.com/ru/post/fr429670/


All Articles