[Pesquisar este blog]

segunda-feira, 20 de novembro de 2017

Java 9::O Console jshell

Estreia no Java uma nova interessante ferramenta de linha de comando denominada jshell, o console Java. Por meio dela é possível avaliar expressões, efetuar declarações e executar diretivas do Java, sem a necessidade de construir um projeto, um programa ou mesmo um método para seu teste.

Este post é parte da série Java 9::Primeiras Impressões, que comenta as principais características da nova versão do Java.

REPL

A abreviatura REPL significa Read-Evaluate-Print-Loop, uma referência a consoles web e plug-ins de diversas linguagens, onde o usuário fornece uma expressão ou diretiva da linguagem ao console (leitura), a qual é processada imediatamente (avaliação), tendo seus resultados exibidos (impressão), retornando à situação inicial (loop) onde uma outra expressão ou diretiva pode ser interativamente fornecida.

Conforme as características próprias de cada um dos consoles que existem, os resultados de avaliações anteriores podem ou não estar disponíveis, possibilitando a acumulação de efeitos e o uso de tais ambientes tanto para experimentação de coisas simples, como para simulação de construções mais complexas, tudo de maneira bastante direta. Esta é a grande vantagem do uso dos consoles REPL.

Como veremos, o jshell permite tanto executar expressões simples, como definir variáveis, instanciar objetos e executar trechos relativamente sofisticados de código, tornando-se igualmente apropriado para o estudante e para o programador mais experiente.

Acionando o jshell

Em um prompt de comandos, console ou terminal, dependendo do seu sistema operacional, garanta que o path está corretamente ajustado. No MS Windows usualmente basta executar:

C:\Users\Jandl>path=C:\Program Files\Java\jdk-9\bin;%path%

No meu caso, o JDK da versão 9 está instalado em C:\Program Files\Java\jdk-9\. O jshell, e as demais ferramentas de linha de comando do JDK estão no subdiretório bin. Após o ajuste do path, basta acionar o comando jshell, como mostra a figura que segue.
Prompt de comando e o acionamento do jshell

Experimente algo simples, como somar dois valores inteiros, pressionando ENTER ao final:

Observe que a expressão “1 + 2” foi avaliada, de modo que o resultado foi armazenado na variável temporária denominada $1, a qual foi exibida. Fornecendo apenas o nome de tal variável, obteríamos o seu conteúdo.

Os ponto-e-vírgula, exigidos no código Java, podem ser omitidos, visto que o jshell, frequentemente, é capaz de adicioná-los.

Testando diretivas e declarações

Diretivas simples, de repetição ou decisão, também podem ser executadas, bastando digitar o código que se deseja avaliar, pressionado ENTER para seu processamento:

Se desejado, variáveis de qualquer tipo válido podem ser declaradas:

O comando /vars permite visualizar todas as variáveis válidas previamente definidas, assim como seus tipos e conteúdos:

Eventualmente, o fragmento de código que se deseja testar fica melhor organizado se escrito em várias linhas. Para isto basta digitar uma linha e, com SHIFT+ENTER, continuar na linha seguinte, como na figura abaixo:

Histórico de comandos: com as setas UP e DOWN do teclado é possível navegar pelas lista de fragmentos anteriormente processados pelo jshell. Após escolher o fragmento desejado, basta um ENTER para executá-lo.

Caso você deseje modificar um comando anterior, ou corrigir uma linha com código errado, basta usar as setas UP e DOWN do teclado para selecionar tal linha, usando as setas LEFT e RIGHT, HOME e END para navegar na linha. Se for um trecho de várias linhas, é necessário repetir todas as linhas corretas, mais as corrigidas, na sequência desejada.

O comando /list lista todos os fragmentos de código válido que foram avaliados.

Use o comando /<id> para executar o fragmento identificado pelo id correspondente ou /! para repetir a execução do último fragmento processado. Lembre-se apenas que os vários fragmentos utilizam as variáveis definidas de maneira global, ou seja, todos os fragmentos têm acesso à todas as variáveis, propagando seus efeitos por meio delas.

Características avançadas

Também é possível instanciar objetos, declarar e inicializar arrays:

Como esperado, objetos de qualquer tipo, assim como arrays, podem ser usados em fragmentos de código, como este que segue, que utiliza o objeto StringBuilder e o array de String declarados acima:

Com o jshell é possível declarar-se métodos para uso independente ou em outros fragmentos.

Até mesmo novos tipos (classes, interfaces ou enumerações) podem ser definidos.

Desta maneira, objetos dos tipos existentes ou definidos no ambiente jshell podem ser instanciados e utilizados.




Comandos do jshell

O jshell suporta a execução de vários comandos, todos precedidos por /, que podem facilitar sua utilização. A tabela que segue mostra os principais comandos em suas formas mais simples:

Comando
Efeito
/list
Lista os fragmentos válidos fornecidos.
/edit <id>
Edita o fragmento identificado por <id>.
/drop <id>
Remove o fragmento identificado por <id>.
/save <fileName>
Salva os fragmentos de código no arquivo indicado.
/open <fileName>
Abre o arquivo contendo fragmentos de código.
/vars
Lista as variáveis declaradas e seus valores.
/methods
Lista os métodos declarados e suas assinaturas.
/types
Lista os tipos declarados.
/history
Lista o histórico do código digitado.
/<id>
Executa o fragmento identificado por <id>.
/!
Reexecuta o último fragmento válido.
/help
Exibe informação sobre o jshell e seus comandos
/exit
Finaliza o jshell.

Considerações Finais

O jshell é, realmente, uma ótima ferramenta, pois alia a simplicidade e conveniência de uso com muita flexibilidade. Como permite testar declarações, diretivas e expressões, incluindo a definição de métodos independentes e de tipos completos, é muito útil para que deseja estudar Java, pois permite praticar a construção de código limitando-se ao essencial. Mesmo programadores mais experientes podem se beneficiar do seu uso para simular e testar fragmentos de código de maneira rápida.

É isso!
/exit


Para saber mais

domingo, 19 de novembro de 2017

Java 9::Métodos Privados em Interfaces

Uma das novidades da versão 9 do Java são os métodos com visibilidade privada nas interfaces. Este novo elemento, acrescido aos métodos default e estáticos incluídos na versão 8, vem para completar as alternativas construtivas nas interfaces.

Este post é parte da série Java 9::Primeiras Impressões, que comenta as principais características da nova versão do Java.

Interfaces

Uma interface é a definição de conjunto de elementos visíveis, ou seja, públicos, dos objetos de uma classe. Quando a implementação de uma classe contém todas as operações de conjunto particular, dizemos que tal classe realiza tal interface, ou seja, esta classe atende as especificações ditadas pela interface implementada.

Uma interface Java é algo simples como:

package jandl.j9;
public interface Adder {
void add(Number value);
Number getTotal();
void setTotal(Number initValue);
void reset();
}

Implicitamente, todas as operações de uma interface são, por padrão, públicas e abstratas, ou seja, todas poderiam receber o especificador de acesso public e o modificador abstract em suas declarações.

Desta maneira, uma interface é como um contrato, que dita quais operações devem estar disponíveis para obter-se um conjunto específico de funcionalidade (e de interoperabilidade, portanto). Isto é particularmente importante, pois a questão aqui é como os objetos podem ser utilizados, e não como foram implementados.

A classe AdderImpl que segue é uma implementação possível da interface Adder.

package jandl.j9;
public class AdderImpl implements Adder {
private double total;
public AdderImpl() { reset(); }
public AdderImpl(Number value) { setTotal(value); }
@Override
public void add(Number value) { total = total + ((Number) value).doubleValue(); }
@Override
public Number getTotal() { return new Double(total); }
@Override
public void setTotal(Number initValue) { total = ((Number) initValue).doubleValue(); }
@Override
public void reset() { total = 0; }
}

Desta maneira, classes pertencentes a diferentes hierarquias de objetos, mas que implementam uma interface comum, podem, polimorficamente, serem tratadas como de um mesmo tipo que corresponde a tal interface.

A modelagem de sistemas por meio da definição de interfaces é uma prática reconhecidamente boa, também conhecida como programação por contratos.

Assim, o uso de interfaces é particularmente importante no projeto de sistemas, o que também é enfatizado pelos princípios SOLID (discutidos numa série de posts iniciados por Os Princípios SOLID – Parte I).

Evolução de interfaces

Apesar das muitas conveniências no projeto e uso de interfaces, surgem situações onde uma interface necessita ser alterada. Aí o cenário deixa de positivo. Modificar uma interface, no sentido de alterar a assinatura de um dos métodos presentes ou acrescentar novas operações cria a exigência de modificar ou completar todas as classes que realizam tal interface, numa constrangedora propagação das alterações realizadas.

A partir do Java 8 se tornou possível realizar alterações numa interface existente, mantendo a compatibilidade com suas versões anteriores, isto é, sem propagar as alterações, simplificando muito o trabalho de manutenção do código. Para isto foram introduzidos os métodos default e estáticos às interfaces.

Os métodos default são aqueles declarados com o especificador de visibilidade public e com o novo modificador default. Além disso, tais métodos devem ser implementados na própria interface, pois não são abstratos, o que evita a propagação da modificação.

A interface Adder poderia receber um novo método, como segue, sem que as classes que a realizam sejam afetadas.

package jandl.j9;
public interface Adder {
:
// método default, cuja implementação é adicionada na interface modificada
default void add(Adder adder) {  add(adder.getTotal()); }
}

De forma análoga, os métodos estáticos são aqueles declarados com o especificador de visibilidade public e com o conhecido modificador static, o que também exige sua própria interface, pois, como qualquer elemento estático, pertencem a seu tipo e não a suas instâncias. Da mesma forma sua adição não propaga qualquer efeito nas classes que já realizam a interface modificada.

package jandl.j9;
public interface Adder {
:
// método estático, cuja implementação é adicionada na interface modificada
static void add(Adder adder, List<Number> list) {
    list.stream().forEach((value)->{ adder.add(value); });
}
}

As duas alternativas, dos métodos default e dos métodos estáticos, possibilitam a evolução de interfaces existentes, sem propagação de alterações e com garantia da compatibilidade binária com suas versões antigas.

Métodos privados em interfaces

A adição da possibilidade de declarar métodos privados em interfaces, embora estranha à primeira vista, é um complemento para auxiliar o programador no uso dos métodos default e métodos estáticos nas interfaces.

Os novos métodos privados são declarados com o especificador de visibilidade private e, opcionalmente, com o modificar static. Como qualquer membro privado, só podem ser acessados pelos demais membros do seu tipo.

Sendo assim, o uso de métodos privados, estáticos ou não, numa interface tem como objetivo permitir que o programador defina operações auxiliares para os demais métodos default e estáticos públicos, sem expor tal operação.

Na nova implementação da interface Adder, um método privado estático add(Adder, Number[]) provê o serviço de adição para três outros métodos da interface.

package jandl.j9;
import java.util.List;

public interface Adder {
void add(Number value);
Number getTotal();
void setTotal(Number initValue);
void reset();
default void add(Adder adder) { add(adder.getTotal()); }
// métodos default e estáticos que utilizam método privado desta interface
default void add(Number[] array) { add(this, array); }
default void add(List<Number> list) { add(this, list.toArray(new Number[0])); }
static void add(Adder adder, List<Number> list) {
   add(adder, list.toArray(new Number[0]));
}
// método estático privado que prove serviço para os demais
private static void add(Adder adder, Number[] array) {
   for(int i=0; i<array.length;i++) { adder.add(array[i]);
}
}
}

Considerações Finais

As interfaces na versão 9 Java suportam, então, uma variada combinação de especificadores e modificadores, como sumarizado na tabela que segue:

Especificador
Modificador
Situação
Nenhum
Nenhum
Declaração válida de método, implicitamente, público e abstrato.
Nenhum
abstract
Declaração válida de método abstrato, implicitamente público.
Nenhum
default
Declaração válida de método default, implicitamente público.
Nenhum
static
Declaração válida de método estático, implicitamente público.
public
Nenhum
Declaração válida de método público, implicitamente abstrato.
public
abstract
Declaração válida explícita de método público e abstrato.
public
default
Declaração válida explícita de método público e default.
public
static
Declaração válida explícita de método público e estático.
private
Nenhum
Declaração válida explícita de método privado.
private
abstract
Declaração inválida. Erro de compilação.
private
default
Declaração inválida. Erro de compilação.
private
static
Declaração válida explícita de método privado e estático.

Os métodos privados não são herdados por subinterfaces ou implementações das classes.
Finalmente, apesar da utilidade exemplificada, de permitir modificações em interfaces existentes sem a propagação de efeitos colaterais indesejados, cabe destacar que, se uma interface existente deve ser modificada, isto indica duas situações: alterações nas regras de negócio da aplicação ou identificação de falhas no projeto do software. Enquanto a primeira razão pode ser imprevisível e inevitável; a segunda reforça o cuidado necessário com o projeto de qualquer software.

A implementação de métodos (default ou estáticos, públicos ou privados) é, do ponto de vista da Orientação a Objetos, uma violação dos seus propósitos. Se é necessária uma implementação parcial de uma interface, é mais adequada a construção de uma classe abstrata contendo a codificação destas operações, mantendo a interface como uma classe abstrata pura (sem implementação de qualquer operação).

Reforço que isto não é um defeito do Java, mas, acredito, alternativas possíveis na plataforma para solução de problemas envolvendo interfaces.

Assim, muito cuidado no projeto de suas interfaces, que devem conter apenas as operações minimamente necessárias (tal como defendido pelo princípio de segregação das interfaces ou ISP dos princípios SOLID). Qualquer coisa diferente disso, pode não ser adequada, mesmo com todas as possibilidades ofertadas pela plataforma Java.

Para saber mais




Java 9::Primeiras Impressões

A versão 9 da plataforma Java está disponível faz alguns meses, com adição de algumas novas características e a, tradicional, evolução de sua API, com mudanças importantes, mas nada revolucionário em relação ao que já existe. Agora é a hora de verificarmos o quanto as novidades são úteis no dia-a-dia.

A lista de features esperada na versão 9 incluiu elementos que podem ser organizados em três categorias, conforme seu escopo, ou seja, aqueles voltados para a linguagem de programação, para o Java Development Kit (JDK) e para a plataforma em si.

Este post contém comentário gerais sobre os novos elementos plataforma 9, alguns dos quais são tratados com mais detalhes nos seguintes posts complementares:

Modificações na Linguagem de Programação

As modificações inclusas linguagem de programação, na verdade, concentram-se no aperfeiçoamento das interfaces, com a adição de métodos privados.

Métodos privados em interfaces [post]

Recordando rapidamente, o Java 8 introduziu a possibilidade incluir métodos públicos default e públicos estáticos em interfaces. Estes novos tipos de métodos exigem implementação na própria interface, diferentemente dos demais métodos públicos abstratos que devem ser implementados nas classes que realizam a interface. Na prática, com isso, permite que interfaces existentes sejam atualizadas, recebendo novas operações, sem provocar a propagação da mudança, pois as classes que já as implementam não sofrem qualquer exigência adicional. Detalhes e exemplos do uso dos métodos default e static em interfaces estão disponíveis no post Interfaces::criação,uso e atualização no Java 8
.
Na versão 9 foram introduzidos os métodos privados nas interfaces, declarados com o especificador de visibilidade private e, opcionalmente, com o modificador static. Como outros membros privados, só podem ser acessados por aqueles presentes em seu tipo.

Um exemplo esquemático de um método privado estático seria:

public interface J9News {
private static boolean validate() { ... }
}

O uso de métodos privados, estáticos ou não, numa interface permite que o programador defina operações auxiliares para os demais métodos default e estáticos públicos, sem expor tal operação. O post Java 9::MétodosPrivados em Interfaces dá mais detalhes sobre esta nova característica.

Modificações no JDK

Nos próximos dias, serão comentadas algumas das novas características do Java Develpment Kit, tais como:
  • Melhorias na Stream API
  • Melhorias em Optional<T>
  • Extensões do Javadoc [JEP 221, 224, 225, 261]
  • Processador Nashorn [JEP 236]
  • Catálogos XML [JEP 268]
  • Novo cliente para HTTP 2 [JEP 110]
  • Imagens multirresolução [JEP 251]
  • I/O para imagens TIFF [JEP 262]
  • Suporte para Unicode 7.0 e 8.0 [JEP 227, 267]

Melhorias na Stream API [post]

O Java 9 traz alguns complementos úteis para esta API, como novas fontes de dados para as streams, além de algumas novas características. Além da possibilidade de obter um stream sequencia a partir de qualquer uma das coleções, existia no Java 8 um conjunto limitado de possibilidades obtenção destas streams  a partir de elementos de outros tipos externos às coleções, como por exemplo, java.io.BufferedReader.lines().

O Java 9 adiciona algumas novas fontes úteis a esse conjunto, por meio destes novos métodos:
  • java.util.Scanner.tokens()
  • java.util.regex.Matcher.results()
  • java.util.Optional.stream()
Além disso, foram adicionados quatro novos métodos na interface java.util.stream.Stream<T> que são:
  • Stream<T> takeWhile(Predicate)
  • Stream<T> dropWhile(Predicate)
  • Stream<T> ofNullable(T)
  • Stream<T> iterate(T, Predicate< T>, UnaryOperator<T>)
Embora pareça pouco, tais métodos são bastante úteis e flexíveis, como discutido no post Java 9::Melhorias na Stream API.

Melhorias em Optional<T> [post]

Para auxiliar no tratamento da exceção NullPointerException, foi introduzido no Java 8 a classe java.util.Optional<T>. Objetos do tipo Optional são como contêineres (i.e., containers) que podem armazenar um valor de qualquer tipo T ou apenas null. A classe Optional também provê alguns métodos úteis que podem eliminar a verificação explícita da presença de null.

Na versão 9 do Java foram introduzidos métodos novos:
  • public void ifPresentOrElse(Consumer<T> action, Runnable emptyAction);
  • public Optional<T> or(Supplier<Optional<T>> supplier);
O primeiro método, ifPresentOrElse() verifica se um valor está presente na instância de Optional<T>, conduzindo a ação indicada com o valor, ou realizando outra para a situação de vazio (conteúdo null).

Outro método interessante é or(), cujo comportamento é o seguinte: se o valor está presente, retorna um Optional que encapsula tal valor, senão, retorna um outro Optional produzido pela função de geração tomada como argumento.

As melhorias em Optional<T> são detalhadas no post Java 9::Melhorias no Optional.

Modificações na Plataforma

A versão 9 também incorporou várias modificações em nível de plataforma, dentre elas:
  • Console Java (jshell) [JEP 222]
  • Sistema de modularização (conhecido como Jigsaw) [JSR 376]
  • JARs multi-release [JEP 238]
  • jlink [JEP 282]
  • Registro unificado [JEP 158]
  • Microbenchmark Suite [JEP 230]
  • Atualização da Process e da Concurrency API [JEP 102 & 266]
  • Coletor de lixo G1 como default [JEP 248]

jshell [post]

O jshell é uma nova e interessante ferramenta de linha de comando que corresponde a um console Java. Diferentemente dos IDEs Java, onde devemos construir classes e programas completos que devem ser compilados para poderem ser executados; no jshell é possível avaliar expressões, efetuar declarações e executar diretivas do Java, sem a necessidade de construir um projeto, um programa ou mesmo um método para seu teste.

O fato de constituir um ambiente interativo torna o jshell muito interessante para que iniciantes estudem o Java experimentando suas construções. Ao mesmo tempo é útil para programadores mais experientes, pois é um ambiente de simulação rápido e direto. Mais detalhes sobre o jshell estão no post Java 9::O Console jshell.

Jigsaw [post]

A característica mais esperada da versão 9 do Java é o resultado do projeto Jigsaw, traz um novo sistema de modularidade para plataforma Java. A modularidade é um princípio importante no projeto de software que enfatiza a criação de conjuntos de classes ou componentes que possam ser reutilizados em diferentes contextos. Até a versão 8, os arquivos JAR (Java Archives) eram a unidade básica da modularidade no Java. Os JARs são arquivos compactados contendo uma estrutura de subdiretórios, arquivos de classe Java e outros arquivos usados como recursos da aplicação.

Embora os arquivos JAR sejam capazes de agrupar classes relacionadas, sua organização possui algumas limitações, como o encapsulamento fraco e a existência de dependências explícitas entre seus elementos, além de problemas associados a presença de JARs de versões diferentes no mesmo classpath.

Por conta disso foi criado um novo artefato, o module, que estabelece uma nova unidade de modularização, resolvendo grande parte dos problemas exibidos pelos packages, e ainda mantendo compatibilidade com aplicações de versões anteriores da plataforma. O Jigsaw é apresentado e comentado no post Java 9: Jigsaw.

jlink

O jlink é uma nova ferramenta de linha de comando que faz parte do novo sistema de modularização do Java. Seu propósito é empacotar os diversos módulos de uma aplicação, facilitando sua utilização e, principalmente, sua distribuição. A ferramenta jlink é abordada juntamente com os posts sobre o Jigsaw.

Mais à frente, comentaremos sobre outras das interessantes características adicionadas na plataforma nesta versão 9.

Considerações Finais

Depois de dissecarmos muitas das novas características da versão 9 do Java, fica claro o seu amadurecimento. É claro que poucos desenvolvedores utilizarão à fundo todas estas adições, concentrando-se nos elementos mais próximos à suas áreas de atuação. Além disso, veremos críticas sobre esta ou aquela característica, assim como elogios sobre outras, mas seria possível garantir a satisfação de todos em relação a tudo.

O que vale mesmo, é que o Java, a cada versão, mostra seu valor!