Script Java! = JavaScript. Cinco javas em uma classe. Nós escrevemos para nos lembrarmos para sempre


Nesta semana, o grupo JUG.ru provavelmente divulgará um anúncio. Até eu dizer o que. A participação em projetos secretos desperta a criatividade, então aqui está outro vídeo noturno sobre Java.

Notícias incríveis: agora não há uma hora e meia de duração, mas cerca de 20 minutos, e ainda há algo para assistir. Um pouco menos do que completamente, consiste em um screencast. Quem não suporta esse lixo de vídeo e gosta de consumir descriptografia de texto, teve que cortar muito texto após o corte. Welkam, e Java pode estar com você.

Em breve, 12 java serão lançados, e muitos ainda estão sentados em Semerochka e acreditam que, se atualizarem, não verão algo particularmente novo ou interessante. Nesta edição super curta, aprenderemos como transformar a vida de nossos colegas no inferno com a ajuda do Java com script e, em alguns lugares, vou estragar novos gadgets. Bem, os obscurantistas, é claro, não serão salvos do progresso.

Diga-me, por que você está atualizando sua ideia? Aparecem constantemente novos recursos, alguns novos atalhos, aumentando dez vezes a sua produtividade como desenvolvedor. Mas você não sabe sobre eles. Você está realmente lendo esta notícia? Se você é um usuário médio, provavelmente não. Você, em princípio, não se importa com a maior parte do tempo. Você está atualizando a idéia simplesmente porque ... você pode. Porque as peles são lindas, o tema sombrio, a barra de toque do MacBook. Mas essa explicação não falha diante das autoridades como a resposta "por que comprar a idéia".

Em vez disso, podemos dizer que, em 29 de outubro, mais recentemente, o JetBrains fixou o suporte para linhas brutas na versão beta do Idea . A ideia está na versão beta, as linhas estão na pré-visualização, mas isso não é mais importante para ninguém. Se você é tão teimoso que coloca 12 java, tem problemas mais sérios. Eu sei por mim mesmo.

Vamos ver como fica na prática. Para fazer isso, tente resolver algum tipo de problema de demonstração. Muitas vezes há o problema de criar scripts para algo. Por exemplo, em jogos de computador, essas são missões, em jenkins, são scripts de construção e assim por diante. Geralmente, Python ou Groove é usado para isso, e vamos usar Java nu! Porque Porque nós podemos, em três linhas, e mesmo sem hacks. Parece uma ótima idéia :-)

Onde assistir


Tudo está no github .

Âmbito de aplicação


Imagine que temos um arquivo como este:

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

Queremos que ele seja executado não ao compilar o aplicativo inteiro, mas como uma string - após o lançamento. Como um script, é isso.

Manualmente


Primeiro você precisa ultrapassar tudo em uma string.

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

Todas essas "\ n" vantagens e recuos parecem extremamente infelizes.

Anteriormente, tudo o que podíamos fazer era colocar o código em um arquivo e lê-lo. Esta é provavelmente uma boa solução, mas nem sempre é adequada. Por exemplo, você é um palestrante em uma conferência e demonstra o código dos slides. Mesmo a construção acima é muito melhor do que apenas uma referência não fundamentada de que você tem um código em algum lugar que faz algo lá. A troca de slides consome tempo e atenção do público. Bem e assim por diante. Em suma, casos, quando você precisar do código exatamente em linha, poderá criar.

Agora temos a oportunidade de nos livrar do lixo usando strings cruas. Levantamos o cursor no código, pressione Alt + Enter (ou o que for executado nos sistemas operacionais Quck Fix no Idea). Selecione "converter para literal de cadeia de caracteres brutos" e obtenha esta 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"; 

Na minha opinião, por causa desse recurso, já vale a pena correr para instalar o JDK 12.

A propósito, para que o recurso funcione, você precisará fazer várias coisas:

  • Faça o download do JDK 12 e registre-se nas configurações do projeto
  • Nas configurações globais de javac, defina o sinalizador --release , bytecode versão 12, sinalizadores adicionais --enable-preview -Xlint:preview
  • Agora, em qualquer configuração de execução / depuração, você precisa adicionar o sinalizador da VM --enable-preview

Se você não entende, como é feito, veja meu screencast no cabeçalho da postagem, tudo fica bem claro lá.

Agora, para começar, você precisa executar três etapas simples: transforme a string em uma representação interna conveniente da fonte, compile-a e execute:

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

Existe uma classe SimpleJavaFileObject para "representação conveniente", mas possui um recurso interessante. É crutalmente abstrato. Ou seja, seu método principal, que a fonte compilada deve retornar, sempre lança uma execução na esperança de que a subclassesemos:

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

Então você tem que escrever um pouco do seu herdeiro. Observe que o construtor SimpleJavaFileObject original deseja um URI para a classe compilada, mas onde o obtemos? Portanto, sugiro apenas colocá-lo da maneira mais óbvia, como aqui na função 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; } 

Agora vamos à compilação:

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

Observe: passamos quatro sinalizadores para montagem, três dos quais são responsáveis ​​por prescrever exatamente as mesmas opções que fizemos com o mouse nas configurações de javac na Idea.

E, finalmente, execute nossa classe temporária:

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

Observe que var clss = loader.loadClass de escrever do que a Class<?> clss = loader.loadClass e não apresenta novos vorings. A palavra-chave var apareceu no Top Ten.

Observe também que clss.newInstance() proposto que clss.newInstance() seja cortado a partir de Nine. Ele engole exceções, o que é ruim. O Nine sugere primeiro chamar getConstructor , que é parametrizado e lança as exceções corretas.

Observe também que chamei a variável de class palavra, mas Java não jurou. Lição de casa: você pode fazer isso de várias maneiras, precisa entender qual é a mais interessante e por quê.

Bem, em geral, isso é tudo. Isso funciona.

Automação


Aqui o crítico exclama: tudo está em preto aqui, porque existem bibliotecas no mundo Java que compilam tudo para você em uma linha.

OK, vamos dar uma olhada. Aqui está o código na biblioteca JOOR, localizada na parte superior do 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()); } } 

Como se estivesse tudo bem. De fato, em uma linha, exceto que eu tive que arrastar o saplayer.

Mas há uma nuance. Tente retornar "Olá Habr!" como literal de string bruto:

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

Tudo irá falhar instantaneamente com o erro "(use --able-preview para ativar literais de strings brutos)". Mas nós já o ativamos? Sim, para o inferno com dois. Nós o incluímos no Idea, e o JOOR o constrói com um compilador de sistema! Vamos dar uma olhada no que há dentro:

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

E quanto a nós quando chamamos o mesmo 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); 

E no JOOR, existe apenas um nulo, em vez de opções. Eles nem podem passar por dentro!

Provavelmente, é uma boa idéia enviar uma solicitação de recebimento ao Github no JOOR. Se eu não vou fazer isso sozinho, você faz, não é?

E a moral é simples, tenha medo de presentes de código aberto. Às vezes, é mais fácil escrever uma pequena parede de texto, mas ter controle sobre ela.

Maneira mais fácil


Existe uma maneira de não escrever uma parede de texto e não usar bibliotecas suspeitas. A partir do Java 9, temos um shell interativo (semelhante ao do Python, Ruby e outras linguagens de script) que pode executar comandos Java. E, claro, está escrito no próprio Java e está disponível como uma classe Java. Você pode criar uma instância e simplesmente executar a atribuição necessária diretamente:

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

Os streams apareceram no Java 8 e agora são usados ​​em todos os lugares, e você nem precisa chamar stream() , outros o chamam por você. Nesse caso, chamar as variables() retorna exatamente o fluxo, não o ArrayList, como alguém de uma infância difícil teria feito. O resultado do fluxo pode ser imediatamente derramado em var .

Observe que agora você pode escrever var também nos parâmetros lambda. Esse recurso está presente desde o Java 11.

Em geral, esta é uma classe muito significativa. Ele usa vários recursos de diferentes gerações de Java e tudo isso parece holística e harmoniosamente. Estou certo de que essas coisas serão usadas por todos e em qualquer lugar, portanto o código no Java 12 será diretamente visualmente diferente do que tínhamos no Java 7.

Resumir


Vimos alguns recursos:

  • 8: Stream (para aqueles no tanque)
  • 9: JShell e novos métodos descontinuados
  • 10: palavra-chave var
  • 11: Desenvolvimento de Var para parâmetros lambda
  • 12: Literais de cadeia bruta e seu suporte no IntelliJ IDEA

Um pequeno aviso para iniciantes. Se você constantemente faz isso em aplicativos reais sem nenhum significado especial, seus colegas ficam loucos a princípio, tentando entender o que você escreveu, e então eles podem vencê-lo com muita dor. E não estou falando sobre novos recursos Java agora, mas sobre compilação em tempo de execução, é claro.

Se você quiser aprender mais sobre Java como uma linguagem, deverá, por exemplo, examinar os relatórios de Tagir Valeev. Você o conhece como um cara do topo do hub Java em Habré, lany . No Joker, ele falou sobre Amber , e na JPoint - seus famosos quebra - cabeças , e assim por diante. Lá, e sobre a linguagem Java e sobre o IntelliJ IDEA, tudo está lá. Tudo isso pode ser encontrado no YouTube . Na verdade, por ciúmes de Tagir, que pode dizer algo sobre a própria linguagem, mas não o faço, e este post acabou. Também haverá novos curingas e o JPoint, mas discutiremos isso mais tarde.

Eu cumpri meu dever moral, um dever para Java. Do meu lado, as balas voaram. E você aí, nos sete, que não tem o décimo segundo Java, fique aqui, tudo de bom, bom humor e saúde. Obrigada

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


All Articles