Java Script!= JavaScript。 一堂课有五个Java。 我们编写脚本以便我们永远记住


JUG.ru集团可能会在本周发布公告。 直到我说什么。 参与秘密项目可以激发创造力,因此这是另一个有关Java的夜间视频。

令人难以置信的消息:现在已经不是一个半小时,而是大约20分钟,甚至还有一些值得关注的地方。 它由一个截屏视频组成,略少于全部。 那些无法忍受此视频垃圾并喜欢使用文本解密的人,在剪切后不得不剪切很多文本。 Welkam,也许Java与您同在。

即将发布12种Java,许多人仍然坐在Semerochka上,他们相信在升级的情况下,他们不会看到任何特别新颖或有趣的东西。 在本期超短篇文章中,我们将学习如何在脚本化Java的帮助下将同事的生活变成地狱,并且在几个地方,我将介绍一些新工具。 好吧,蒙昧主义者当然不会从进步中幸免。

告诉我,您为什么要更新您的想法? 新功能不断出现,一些新的快捷方式,使开发人员的工作效率提高了十倍。 但是你不知道他们。 你真的在读这个新闻吗? 如果您是普通用户,则可能不是。 原则上,您在大多数情况下都不会对您造成伤害。 您之所以更新想法,仅仅是因为……可以。 因为皮肤很漂亮,所以是深色主题,是MacBook上的触摸栏。 但是,这种解释在当局面前并没有作为“为什么要买这个主意”的答案。

取而代之的是,可以说是10月29日,即最近,JetBrains在Beta版的Idea中固定了对原始行的支持。 这个想法是在beta中,行在预览中,但这对任何人都不再重要。 如果您如此固执,以至于只放了12个Java,那么您将遇到更严重的问题。 我自己知道。

让我们看看它在实际中的外观。 为此,请尝试解决某种示范问题。 经常有脚本编写问题。 例如,在计算机游戏中,这些是任务,在詹金斯中,这些是构建脚本,依此类推。 通常,为此使用Python或Groove,让我们开始使用裸Java! 为什么,为什么? 因为我们可以做到三行,甚至没有黑客。 听起来是个好主意:-)

在哪里看


一切都在github上

适用范围


假设我们有一个这样的文件:

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

我们希望它不是在编译整个应用程序时执行,而是在启动后作为字符串执行。 就像脚本一样。

手动地


首先,您需要将所有内容都放入字符串中。

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

所有这些“ \ n”加号和缩进看起来非常惨。

以前,我们所能做的就是将代码放在文件中并读取。 这可能是一个很好的解决方案,但并不总是适用。 例如,您是会议的发言人,并演示幻灯片中的代码。 即使是上面的构造,也比仅是未经证实的参考要好得多,因为您的代码在某处可以执行某些操作。 切换幻灯片会浪费时间和观众的注意力。 等等。 简而言之,当您需要完全内联的代码时,就可以提出。

现在我们有机会使用原始字符串摆脱垃圾。 我们将光标放在代码上,然后按Alt + Enter(或在Idea中的OS Quck Fix上运行的任何命令)。 选择“转换为原始字符串文字”并获得以下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"; 

我认为,就此功能而言,已经值得运行JDK 12。

顺便说一句,为了使该功能正常运行,您需要做几件事:

  • 下载JDK 12并注册项目设置
  • 在全局javac设置中,设置--release标志,字节码版本12,其他标志--enable-preview -Xlint:preview
  • 现在,在任何运行/调试配置中,您需要添加VM标志--enable-preview

如果您不明白它的完成方式,请从帖子标题中查看我的截屏视频,那里的一切都非常清楚。

现在,开始,您需要执行三个简单的步骤:将字符串转换为源的便捷内部表示形式,对其进行编译并运行:

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

有一个用于“便捷表示”的SimpleJavaFileObject类,但是它具有一个有趣的功能。 它是非常抽象的。 也就是说,其已编译源应返回的键方法始终引发执行,以希望我们将其子类化:

  /** * 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(); } 

因此,您必须写一些继承人。 请注意,原始的SimpleJavaFileObject构造函数需要已编译类的URI,但是我们从哪里得到呢? 因此,我建议以最明显的方式粘贴它,例如buildURI函数中的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; } 

现在让我们继续编译:

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

请注意:我们传递了四个组装标志,其中三个标志负责在Idea的javac设置中指定与使用鼠标所做的完全相同的选项。

最后,运行我们的临时课程:

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

请注意, var clss = loader.loadClassClass<?> clss = loader.loadClass var clss = loader.loadClass编写,并且不会引入任何新的变化。 var关键字出现在前十名中。

另请注意,建议从9开始削减clss.newInstance() 。 他吞下异常,这很糟糕。 九人建议首先调用getConstructor ,该方法已参数化并抛出正确的异常。

还请注意,我将变量称为class ,但是Java却没有誓言。 作业:您可以通过多种方式来完成作业,您需要了解哪种方式最有趣以及为什么。

好吧,总的来说,仅此而已。 可以用

自动化技术


评论家会在这里惊叹,这里的所有内容都是黑色的,因为Java世界中有一些库可以一站式为您编译所有内容。

好,让我们看一下。 这是位于Google顶部的JOOR库中的代码:

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

好像一切都很好。 的确,除了我不得不拖动saplayer之外,一行都可以。

但是有细微差别。 尝试返回“ Hello Habr!” 像原始字符串文字一样:

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

一切都会立即崩溃,并显示错误“(使用--able-preview启用原始字符串文字)”。 但是我们已经打开了吗? 是的,两个地狱。 我们将其包含在Idea中,然后JOOR使用系统编译器进行构建! 让我们看看里面是什么:

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

当我们自己调用相同的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); 

而在JOOR中,仅有一个空null而不是选项。 他们甚至无法通过里面!

在JOOR中向Github发送这样的拉取请求可能是一个好主意。 如果我自己不想做,那就去吧?

而且道德很简单,不要怕开源礼物。 有时候,写下一小段文字会比较容易,但是可以控制它。

最简单的方法


有一种方法不写文字墙,也不使用可疑库。 从Java 9开始,我们有一个交互式外壳程序(类似于Python,Ruby和其他脚本语言的外壳程序),可以执行Java命令。 当然,它是用Java本身编写的,并且可以作为Java类使用。 您可以创建它的实例,然后直接直接执行必要的分配:

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

Streams出现在Java 8中,现在已在各处使用,您甚至不需要自己调用stream() ,其他人会为您调用它。 在这种情况下,调用variables()完全返回流,而不是ArrayList,就像一个困难的七个童年时期的人会做的那样。 流的结果可以立即倒入var

请注意,现在您也可以在lambda参数中编写var 。 自Java 11起,此功能就一直存在。

通常,这是非常重要的一类。 它使用了来自不同Java世代的许多功能,所有这些看起来都是整体和谐的。 我确信这样的事情将被所有人和世界各地所使用,因此Java 12中的代码在视觉上将直接不同于Java 7中的代码。

总结一下


我们研究了一些功能:

  • 8:溪流(用于坦克中的人)
  • 9:JShell和新的不推荐使用的方法
  • 10:var关键字
  • 11:lambda参数的Var开发
  • 12:原始字符串文字及其在IntelliJ IDEA中的支持

对初学者的一点警告。 如果您在没有任何特殊含义的实际应用程序中不断进行此操作,那么您的同事会首先发疯,试图理解您的内容,然后他们可能会非常痛苦地击败您。 而且,我现在不是在谈论新的Java功能,而是在运行时进行编译。

如果您想了解有关Java作为一种语言的更多信息,那么您应该例如查看Tagir Valeev的报告。 您知道他是哈尼 (Habré)Java集线器顶端的家伙。 在Joker,他谈论了Amber ,在JPoint,谈论了他著名的难题 ,等等。 那里,以及有关Java语言和IntelliJ IDEA的所有信息。 所有这些都可以在YouTube上找到 。 实际上,出于对Tagir的嫉妒,他可以说一些关于语言本身的信息,但我没有,这篇文章已经证明了。 也将有新的玩笑者和JPoint,但我们稍后会讨论。

我履行了我的道德义务,这是Java的义务。 从我这边,子弹飞了出来。 在那七个没有第十二个Java的人那里,您一直都很好,心情很好,身体健康。 谢谢啦

Source: https://habr.com/ru/post/zh-CN429670/


All Articles