📜 ⬆️ ⬇️

Java script! = Javascript. Five javas in one class. Scripting to remember forever


This week JUG.ru Group is likely to get an announcement. Until I tell you what. Participation in secret projects wakes up the creative, so here's another night vidosik about java.

Incredible news: now it’s not half an hour long, but about 20 minutes, and there is even something to watch. Slightly less than completely consists of a screencast. Who does not tolerate this video data and loves to consume text transcripts, I had to write a lot of text after the kata. Wellcome, and may Java be with you.

Soon 12 java will be released, and many are still sitting on 7-seo and believe that in the event of an upgrade, they will not see anything particularly new or interesting. In this super-short release, we will learn how to turn the lives of our colleagues into hell with the help of scripting java, and in a couple of places I will screw up new gadgets. Well, the obscurantists, of course, will not be saved from progress.

Tell me, why are you updating your idea? There are constantly getting out new features, some new shortcuts, ten times increasing your productivity as a developer. But you do not know about them. Do you really read this news? If you are an average user, then probably not. You, in principle, most of the time - do not care. You update the Idea simply because ... you can. Because skinny beautiful, dark theme, the touchbar on the MacBook. But such an explanation does not disappear before the authorities as the answer "why buy an idea."
')
Instead, we can say that on October 29, more recently, JetBrains wrote down the support for raw lines in the beta version of Idea . The idea is in beta, the lines are in the preview, but this is not important to anyone. If you are so stupid that you put 12 java, then you have more serious problems. By myself I know.

Let's see how it looks in practice. To do this, try to solve some kind of demo problem. Quite often there is a problem to scripting something. For example, in computer games these are quests, in jenkins, these are build scripts, and so on. Usually, Python or Gruvy is used for this, but let's take and use bare java! Why why? Because we can, in three lines, and even without hacks. Sounds like a great idea :-)

Where to see


Everything is on Github .

Domain


Imagine that we have a file like this:

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

We want it to be executed not when the entire application is compiled, but as a string — after the launch. As a script, that is.

Manually


First you need to overtake everything in line.

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

All these "\ n", plus signs and indents look extremely miserable.

Previously, all we could do was put the code into a file and read it. This is probably a good solution, but it is not always suitable. For example, you are a speaker at a conference and demonstrate the code from the slides. Even the above construction is much better than a mere unsubstantiated reference that you have a code somewhere out there that does something there. Switching slides eats the time and attention of listeners. Well, and so on. In short, cases, when you need a code just inline, you can think of.

Now we have the opportunity to get rid of garbage with the help of raw lines. We get up the cursor on the code, press Alt + Enter (or whatever it is on your OS launches Quck Fix in the Idea). Choose "convert to raw string literal" and get this little thing:

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

In my opinion, for the sake of this feature, it is worth running to install JDK 12.

By the way, in order for the feature to work, you will need to do several actions:


If you do not understand how it is done, see my screencast from the post cap, everything is pretty clear there.

Now, to start you need to perform three simple steps: turn the line into a convenient internal representation of the source code, compile it and run:

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

For “convenient representation”, there is a class SimpleJavaFileObject , but it has one interesting feature. It is a crutch abstract. That is, its key method, which the compiled source should return, always throws an executable in the hope that we will add it:

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

So you have to write some of his heir. Please note that the original SimpleJavaFileObject constructor wants a URI of a compiled class, and where do we get it? Therefore, I propose to simply glue it in the most obvious way as in the buildURI function:

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

Now go to the 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"); } } 

Please note: we pass four flags to build, three of which are responsible for writing exactly the same options that we did with the mouse in the javac settings in the Idea.

Finally, we start our temporary class:

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

Note that var clss = loader.loadClass more convenient for writing than Class<?> clss = loader.loadClass , and does not introduce any new terminations. The keyword var appeared in the Top Ten.

Also note that clss.newInstance() suggested to be cut out starting from Nine. He swallows exceptions, which is bad. Nine is first suggested calling getConstructor , which is parameterized and throws the correct exceptions.

Also note that I called the variable the word class , but Java did not swear. Homework: you can do this in several ways, you need to understand which one is the most interesting and why.

Well, in general, that's all. It is working.

Automation


Here the critic exclaims, everything is in black, because there are libraries in the Java world, which themselves will compile everything into one line.

OK, let's see. Here is the code on the library JOOR, located in the top of 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()); } } 

As if everything is fine. Indeed, in one line, except that I had to drag a saplaer.

But there is a nuance. Try returning “Hello Habr!” As raw string literal:

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

Everything will instantly fall with the error "(use - enable-preview to enable raw string literals)". But we already included it? Yes, hell with two. We included it in the Idea, and JOOR compiles it with the system compiler! Let's see what's inside:

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

And how was it when we called the same 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); 

And in JOOR there just bare null stands instead of options. They can not even be transferred inside!

Probably, it is a good idea to push them to JOOR with such a pull request on GitHub. If I do not get together, you do it, huh?

And the moral is simple, be afraid of the gifts of the bringers. Sometimes it's easier to write a small wall of text, but to have control over it.

The easiest way


There is a way not to write a wall of text, and not to use suspicious libraries. Starting from Java 9, we have an interactive shell (similar to the one in Python, Ruby and other scripting languages) that can execute Java commands. And of course, he himself is written in Java and is available as a Java class. You can create an instance of it and simply perform the necessary assignment directly:

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

Stream appeared in Java 8, and now they are used everywhere, and you don’t even have to call stream() yourself, others will call for you. In this case, the variables() call returns exactly the stream, and not the ArrayList, as someone from a difficult seven-year childhood would do. The result of the stream can immediately pour into the var .

Please note that now you can write var also in lambda parameters. This feature has been with us since Java 11.

In general, this is a very significant class. It uses a bunch of features from different generations of Java, and it all looks holistic and harmonious. I’m sure that such things will use everything and everywhere, so Java 12 code will be visually distinct from what we had in Java 7.

Summing up


We looked at several features:


A little warning for newbies. If you constantly do this in real applications without any special meaning, then colleagues will go crazy first, trying to understand what you have written, and then they can be very painful to beat. And this is not about the new features of Java, but about the compilation in runtime, of course.

If you want to learn more about java as a language, then you should, for example, look at the reports of Tagir Valeev. You know him as a dude from the top of the Java hub in Habré, lany . On Joker, he talked about Amber , and at the JPoint - his famous puzzle-makers , and so on and so forth. There and about the Java language, and about IntelliJ IDEA, everything is there. All this can be found on YouTube . Actually, from the envy of Tagir, who can tell something about the language itself, but I don’t, and it turned out this post. And there will be new jokers and JPoint, but we'll discuss this later.

I fulfilled my moral duty, duty to Java. For my part, the bullets flew out. And you are there, on the seven, who do not have the twelfth java, stay here, you all the best, good mood and health. Thank.

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


All Articles