Desenvolvimento

15 nov, 2017

Introdução ao Java 9 – JShell

Publicidade

Hoje vamos falar sobre uma funcionalidade bem interessante: o JShell. É uma ferramenta semelhante às que já existem hoje no mercado em outras linguagens de programação, como: Scala, JavaScript e outras. É um REPL (Read-Eval-Print Loop), ou seja, um terminal interativo que você pode utilizar para rodar código Java sem ter que ficar criando classes e métodos Main para executar seus experimentos e testes de maneira rápida.

Sem mais delongas, vamos ver na prática como o negócio funciona. Mas antes de começar a brincadeira, vamos baixar o JDK, ok? Ele pode ser encontrado neste link.

Assim que a instalação for concluída, apenas verifique se a versão default do sistema é o Java 9. Vamos lá:

➜  ~ java -version
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)
➜  ~

Para iniciá-lo é bem simples: basta digitar jshell em um terminal.

➜  ~ jshell
|  Welcome to JShell -- Version 9
|  For an introduction type: /help intro

jshell>

Inicialmente, como o próprio Shell propõe, você pode acessar alguns exemplos de uso com o comando /help intro.

jshell> /help intro
|  
|  intro
|  
|  The jshell tool allows you to execute Java code, getting immediate results.
|  You can enter a Java definition (variable, method, class, etc), like:  int x = 8
|  or a Java expression, like:  x + x
|  or a Java statement or import.
|  These little chunks of Java code are called 'snippets'.
|  
|  There are also jshell commands that allow you to understand and
|  control what you are doing, like:  /list
|  
|  For a list of commands: /help

jshell>

Ah! E o JShell tem autocomplete. Quando você começar a escrever o nome de uma classe ou método, apertando a tecla TAB, o terminal vai te dar de cara algumas opções, caso exista mais de uma opção para completar sua sentença. No exemplo abaixo, iniciei a escrita da classe String, mas como existem diversas outras classes e interfaces que iniciam com as mesmas letras, o JShell logo trouxe várias sugestões:

jshell> Str
Stream                             	StreamCorruptedException          
StreamSupport                	StreamTokenizer                   
StrictMath                        	String                            
StringBuffer                     	StringBufferInputStream           
StringBuilder                   	StringIndexOutOfBoundsException   
StringJoiner                      	StringReader                      
StringTokenizer                   	StringWriter                      

jshell> Str

Vamos brincar, então. Quem já utilizou algum outro REPL, com certeza vai ficar mais de boa nesses testes. E quem nunca utilizou vai acabar viciando.

Declarar variáveis? Fácil: (tipo) + (nome_da_variavel) = (valor)(ou_ em_branco)

jshell> int x
x ==> 0

jshell>

Note, declaramos uma variável do tipo "int" porém sem valor…. Vamos atribuir um valor pra ela…

jshell> x = 10
x ==> 10

jshell>

Note que, ao atribuir um valor para variável ‘x’, o shell nos mostra o valor dela logo em seguida. Caso queira confirmar, basta digitar ‘x’:

jshell> x
x ==> 10

jshell> 
{code}

OBS: ponto e vírgula no final das sentenças é opcional, ok?

Então vamos mais adiante. Lembra do famoso Hello World? Quem nunca, né? Como fazíamos mesmo? Por ex.: em uma IDE, criávamos um projeto, uma classe (Main.java ou Teste.java) e então o famoso método Main (public static void main(String[] args)). APENAS para “printar” “Hello World” no terminal, não é?

package me.bruno;
public class Main {

	public static void main(String[] args ) {
		System.out.println("Hello Java 9!!");
	}
}

Pois bem, vamos printar Hello Java 9 agora no JShell?

jshell> System.out.println("Hello Java 9!!")
Hello Java 9!!

jshell>

Brincadeira. Claro que, para quem está iniciando, faz o maior sentido trabalhar com uma IDE, saber criar uma classe e saber o que o método main faz, assim como sua estrutura. O JShell, na minha opinião, foi feito tanto para a didática do ensino Java, como para desenvolvedores mais experientes testarem alguns códigos ou APIs de forma mais prática e rápida.

Podemos também criar variáveis de forma implícita, sem precisar dar um tipo ou um nome para ela.

jshell> "This is a new feature of Java 9"
$5 ==> "This is a new feature of Java 9"

jshell>

Note que automaticamente o JShell criou uma variável com o nome $5. Para as variáveis implícitas, o shell sempre atribui um “nome” começando com $ (dollar) e em seguida um ID chamado de snippet, nome que o Jshell dá para os comandos que você executa. Eles vão ficando armazenados na instância do shell e para visualizar, basta digitar /list:

jshell> /list

   1 : int x;
   2 : x = 10
   3 : x
   4 : System.out.println("Hello Java 9!!")
   5 : "This is a new feature of Java 9"

jshell>

Viram? Até agora esses foram os comandos que digitamos. A cada comando uma chave (key ou snippet) é atribuída para o comando. O JShell faz a identificação e a “tipagem” do objeto por trás dos panos. Desta forma, podemos usar métodos desse tipo que foi fixado. Por ex.: no snippet $5, inserimos um valor entre aspas. Isso quer dizer que seu tipo deve ser String. Então, vamos testar.

jshell> $5.contains("Java 9")
$6 ==> true
jshell> $5.replace("This is","Ow Goshh")
$7 ==> "Ow Goshh a new feature of Java 9"

jshell>

Divertido?

Então vamos deixar o negócio mais sério. Criar métodos? Passar parâmetro nos métodos? Sim, por que não? O Jshell interpreta e entende as linhas de código, ele aceita que criemos métodos e até classes.

Vamos criar um método que apenas retorna uma frase:

jshell> String helloJava9() {
   ...> 	     return "Java 9 is very good!!!";
   ...> }
|  created method helloJava9()

jshell>

Conforme vamos escrevendo nosso método, ao apertar enter o shell entende que ainda não é o momento de criar um objeto, um método, uma classe ou o que seja, visto que antes de apertarmos o enter, abrimos o método com “{“, e apenas quando fechamos ele,  “}“, e pulamos a linha que o método será criado. Lembra do comando para visualizar os snippets que já criamos?

jshell> /list

   1 : String helloJava9() {
       return "Java 9 is very good!!!";
       }

jshell>

Note, dentro do snippet 1, o método que acabou de ser criado foi armazenado. E para chamar nosso método, basta digitar o nome dele:

jshell> helloJava9()
$3 ==> "Java 9 is very good!!!"

jshell>

Legal? Vamos agora criar um método com parâmetros, que vamos passar assim que fizermos a sua chamada. Esse método, apesar de ser simples, temos que prestar atenção em sua escrita no terminal, pois pode causar um pouco de confusão até pegarmos prática, tudo bem? Esse método apenas vai fazer um loop 10x a partir do valor passado no parâmetro inteiro.

jshell> void printTenNumbersWithWord(String word, Integer number) {
   ...> if(number >= 10) {
   ...> for(int i = number; i <= (number + 10); i++) {
   ...> System.out.printf("%s -> %s \n", word, i);
   ...> }
   ...> }
   ...> }
|  created method printTenNumbersWithWord(String,Integer)

E nosso método foi criado. Sempre que alguma coisa na escrita estiver inconsistente, o shell dará o feedback da inconsistência. Para testar nosso método, basta chamarmos passando os dois parâmetros: o primeiro: uma string e o segundo um inteiro.

jshell> printTenNumbersWithWord("This is great!!", 20)
This is great!! -> 20 
This is great!! -> 21 
This is great!! -> 22 
This is great!! -> 23 
This is great!! -> 24 
This is great!! -> 25 
This is great!! -> 26 
This is great!! -> 27 
This is great!! -> 28 
This is great!! -> 29 
This is great!! -> 30 

jshell>

E tem mais: caso queira editar seu método, é possível. Basta sabermos qual o ID correspondente à ele. Para isso, basta digitarmos /list e o nome do método que foi criado. OBS: lembre-se que o JShell tem autocomplete.

jshell> /list printTenNumbersWithWord

   1 : void printTenNumbersWithWord(String word, Integer number) {
       if(number >= 10) {
       for(int i = number; i <= (number + 10); i++) {
       System.out.printf("%s -> %s \n", word, i);
       }
       }
       }

jshell>

Veja, o nosso ID é 1 e para editar o método, basta digitar “/edit 1”. Nesse momento um editor padrão vai ser aberto, o “(Jshell Edit Pad)”. É possível trocar o editor padrão (eu particularmente gostei do default), mas caso queira utilizar o VI, EMACs, NANO ou qualquer outro, basta digitar /set editor {e_o_nome_do_editor}. No exemplo, vou de JEP mesmo.

Esta é a interface do editor:

Bem tranquila, certo? Não tem segredo. Para usar, basta realizar as alterações e depois accept; exit para fechar.

Para nossa alteração, lembra que é possível criarmos classes também? Então vamos envolver nosso método com uma classe, vou chamá-la de MeuTeste:

Note que, ao fechar, o editor o shell vai lhe dar o seguinte feedback:

jshell> /edit 1
|  created class MeuTeste

E o conteúdo da minha classe ficou da seguinte forma:

public class MeuTeste {	
	void printTenNumbersWithWord(String word, Integer number) {
		if(number >= 10) {
			for(int i = number; i <= (number + 10); i++) {
				System.out.printf("%s -> %s \n", word, i);
			}
		}
	}
}

E para testar, sem novidades.

jshell> new MeuTeste().printTenNumbersWithWord("This is great!!", 20)
This is great!! -> 20 
This is great!! -> 21 
This is great!! -> 22 
This is great!! -> 23 
This is great!! -> 24 
This is great!! -> 25 
This is great!! -> 26 
This is great!! -> 27 
This is great!! -> 28 
This is great!! -> 29 
This is great!! -> 30 

jshell>

Da mesma forma que podemos editar um snippet salvo, podemos também excluir. Para isso, basta você saber o ID do método, classe ou o que quer que deseje excluir. Como criei uma classe para o método que estávamos testando, vou excluir agora o método que ficou para trás:

jshell> /drop 3
|  dropped method printTenNumbersWithWord(String,Integer)

E lá se foi ele. Existem outros comandos que podem ser úteis para nós: /type (lista classes) | /methods (nem preciso dizer, né?) | /vars

Outra coisa: podemos usar também imports, por isso vamos fazer um teste, alterando o método da classe que criamos. Vai ser algo bem simples: adicionamos no log do nosso console um recurso de data do Java 8. Com isso, conseguimos realizar um import.

public class MeuTeste {	
	void printTenNumbersWithWord(String word, Integer number) {
		if(number >= 10) {
			for(int i = number; i <= (number + 10); i++) {
				System.out.printf("%s -> %s : %s \n", word, i, Instant.now());
			}
		}
	}
}

Veja, apenas adicionei um parâmetro a mais no printf e Instant.now() para pegar o momento de cada print.

Agora vamos chamar essa nossa classe para testar o método.

jshell> new MeuTeste().printTenNumbersWithWord("This is a great!!", 20)
|  attempted to use class MeuTeste which cannot be instantiated or its methods invoked until variable Instant is declared

jshell>

Ops, algo deu errado. O shell reclamou da classe Instant. Será que é por que ela faz parte do pacote java.time.Instant e não fizemos o import dele em nenhum lugar? Como essa classe não faz parte dos pacotes default, vamos importá-la, então.

No terminal, vamos digitar:

jshell> import java.time.Instant;

Feito isso, podemos executar novamente a classe para testar nosso método.

jshell> new MeuTeste().printTenNumbersWithWord("This is a great!!", 20)
This is a great!! -> 20 : 2017-10-03T13:10:18.054258Z 
This is a great!! -> 21 : 2017-10-03T13:10:18.086463Z 
This is a great!! -> 22 : 2017-10-03T13:10:18.086972Z 
This is a great!! -> 23 : 2017-10-03T13:10:18.087365Z 
This is a great!! -> 24 : 2017-10-03T13:10:18.087722Z 
This is a great!! -> 25 : 2017-10-03T13:10:18.088049Z 
This is a great!! -> 26 : 2017-10-03T13:10:18.088371Z 
This is a great!! -> 27 : 2017-10-03T13:10:18.088720Z 
This is a great!! -> 28 : 2017-10-03T13:10:18.089009Z 
This is a great!! -> 29 : 2017-10-03T13:10:18.089318Z 
This is a great!! -> 30 : 2017-10-03T13:10:18.089628Z 

jshell>

Legal, né? Caso queira ver quais pacotes estão importados no shell, é só digitar /import.

jshell> /import
|    import java.io.*
|    import java.math.*
|    import java.net.*
|    import java.nio.file.*
|    import java.util.*
|    import java.util.concurrent.*
|    import java.util.function.*
|    import java.util.prefs.*
|    import java.util.regex.*
|    import java.util.stream.*
|    import java.time.Instant

jshell>

E tem mais! Dá uma olhada nessa documentação do Jshell no site da Oracle. Tem mais um monte de coisas legais lá.

Por hoje é só, pessoal. Espero que tenham gostado. Ah, é claro. Para sair do Jshell, basta digitar /exit.

See you!

***

Este artigo foi publicado originalmente em: https://www.concrete.com.br/2017/10/16/introducao-ao-java-9-jshell/