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. Those who don’t tolerate this video snapshot and love to consume text transcripts 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'll 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 29 October, more recently, JetBrains gashes support raw strings in beta Ideas. The idea is in beta, the lines are in the preview, but this is not important to anyone. If you are already 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, the problem arises 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;
    publicclassHelloHabrPrototype{
        public String get(){
            return"Hello Habr!";
        }
    };
    

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

    Manually


    First you need to overtake everything in a row.

    privatestaticfinal String SOURCE = "\n" +
                "    package io.javawatch;\n" +
                "    public class HelloHabr {\n" +
                "        public String get() {\n" +
                "             return \"Hello Habr!\";\n" +
                "        }\n" +
                "    };";
        publicstaticfinal String CNAME = "io.javawatch.HelloHabr";
        privatestatic 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 in the 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 up.

    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 launches Quck Fix in the Idea on your OS). Choose "convert to raw string literal" and get this little thing:

    privatestaticfinal String SOURCE = `
        package io.javawatch;
        publicclassHelloHabr{
            public String get(){
                 return"Hello Habr!";
            }
        };`;
        publicstaticfinal String CNAME = "io.javawatch.HelloHabr";
        privatestatic 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:

    • Download JDK 12 and register in the project settings
    • In global javac settings, set the flag --release, bytecode version 12, additional flags--enable-preview-Xlint:preview
    • Now in any run / debug configuration you need to add the VM flag --enable-preview

    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:

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

    There is a class for “convenient representation” SimpleJavaFileObject, but it has one curious feature. It is a crutch abstract. That is, its key method, which the compiled source should return, always throws an action 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 {
            thrownew UnsupportedOperationException();
        }
    

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

    publicstaticclassRuntimeSourceextendsSimpleJavaFileObject{
            private String contents = null;
            @Overridepublic CharSequence getCharContent(boolean ignoreEncodingErrors)throws IOException {
                return contents;
            }
            privatestatic RuntimeSource create()throws Exception {
                returnnew RuntimeSource(CNAME, SOURCE);
            }
            publicRuntimeSource(String className, String contents)throws Exception {
                super(buildURI(className), Kind.SOURCE);
                this.contents = contents;
            }
        publicstatic 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:

    publicstaticvoidcompile(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:

    publicstatic 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 clаss = loader.loadClass("io.javawatch.HelloHabr"); // Java 10// Запуск метода рефлекшеном
            Object instance = clаss.getConstructor().newInstance(); // Java 9//        Object instance = clаss.newInstance();
            Method thisMethod = clаss.getDeclaredMethod("get");
            Object result = thisMethod.invoke(instance);
            return (String) result;
        }
    

    Please note that it is var clаss = loader.loadClassmore convenient for writing than Class<?> clаss = loader.loadClass, and does not introduce any new Vorning. The keyword varappeared in the top ten.

    Also note that it is clаss.newInstance()proposed to cut, starting with Nines. He swallows exceptions, which is bad. Nine is offered first to call getConstructor, which is parameterized and throws the correct exceptions.

    Also note that I called the variable a 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 JOOR library located in the top of Google:

    package io.javawatch;
    import org.joor.Reflect;
    import java.util.function.Supplier;
    publicclassAutomatic{
        publicstaticvoidmain(String[] args){
            Supplier<String> supplier = Reflect.compile(
                    "io.javawatch.HelloHabr",
                    `
                    package io.javawatch;
                    publicclassHelloHabrimplementsjava.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!

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

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

    The easiest way


    There is a way and 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 Javas 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:

    publicclassShell{
        publicstaticvoidmain(String[] args){
            var jShell = JShell.create(); //Java 9
            jShell.eval("String result;");
            jShell.eval(`result = "Hello Habr!";`); //Java 12var result = jShell.variables() //Streams: Java 8, var: Java 10
                .filter((@Nullablevar 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 it yourself stream(), others will call for you. In this case, the call variables()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 var.

    Please note that now you can also write varin the parameters of lambda. This feature is 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 just visually different from what we had in Java 7.

    Summing up


    We looked at several features:

    • 8: Stream (for those in the tank)
    • 9: JShell and new deprecated methods
    • 10: The var keyword
    • 11: Var development for lambda parameters
    • 12: Raw String Literals and their support in IntelliJ IDEA

    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 now I’m not talking about new features of Java, but about compiling 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 JPoint - his famous puzzle-makers , and so on and so forth. There both 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 will 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.

    Also popular now: