Java 9 na prática: Inferência de tipos

 

Na semana passada anunciamos no blog da Caelum que estou começando uma série de posts sobre Java 9, pra que todos possam acompanhar as novidades da linguagem até o seu tão esperado lançamento oficial.

Desta vez, escolhi falar sobre uma proposta que ainda não foi aprovada, mas sem dúvida já é uma das mais polêmicas e comentadas. A JEP 286, Local-Variable Type Inference:

“We seek to improve the developer experience by reducing the ceremony associated with writing Java code, while maintaining Java’s commitment to static type safety, by allowing developers to elide the often-unnecessary manifest declaration of local variable types.

 

Melhora contínua na inferência de tipos do Java

A idéia da JEP 286 é continuar o trabalho que já vem sendo feito nas versões anteriores da linguagem, que é diminuir verbosidade, reduzindo a quantidade de informações de tipagem que temos que declarar explicitamente em nosso código.

Quer um bom exemplo? O famoso diamond operator do Java 7:

Map<String, String> mapa = new HashMap<>();

 

Java 7 e o operador diamante

O operador diamante <> nada mais é do que um syntax sugar dos tipos que tinham que ser declarados explicitamente no momento de instanciar objetos genéricos. Até então, o código anterior teria que ser escrito assim:

Map<String, String> mapa = new HashMap<String, String>();

Foi um grande passo, mas a inferência de tipos ainda era bastante limitada.

 

Java 8 e muitas novas possibilidades

No Java 8, novas JEPs foram aprovadas e parte do compilador foi reescrito, melhorando drasticamente sua habilidade de inferir tipos. A introdução das expressões lambdas foram um grande motivador.
Um exemplo seria esse comparator, que mostramos anteriormente em nosso post de Java 8:

palavras.sort(comparing(s -> s.lenght());

Apesar de ser possível, em nenhum momento precisamos declarar que a variável s da expressão lambda era do tipo String. Estamos ordenando uma lista que é do tipo String, portanto isso foi inferido automaticamente.

 

A possível inferência de variáveis locais do Java 9

A proposta do Java 9 foi além, sugerindo que a seguinte sintaxe seja aceita:

var palavras = new ArrayList<String>();
val stream = palavras.stream(); 

Repare no uso do var e do val. A diferença entre essas duas palavras chaves é que a primeira, usada na lista de palavras, representas uma variável local, mutável, do tipo ArrayList. A segunda representa uma variável local imutável, isto é, final.

Seria equivalente a escrever:

final Stream<String> stream = palavras.stream();

 

Mas isso só vai funcionar com listas?

Esse recurso não se limita apenas na declaração de listas e objetos genéricos…

var nome = "Rodrigo Turini";
var count = 10;

Legal, não é? Ele pode ser usado desde uma simples String, um inteiro, ou até mesmo Paths, InputStreams, mapas, sets e outros.

var path = Path.of(fileName);
var fileStream = new FileInputStream(path);
var bytes = Files.readAllBytes(fileStream);

 

E qual é a opinião da comunidade?

O Brian Goetz, um dos arquitetos do Java, publicou uma pesquisa para coletar a opinião da comunidade a respeito dessa proposta. O resultado foi impressionante:

survey result

2.453 pessoas participaram e o entusiamo para adoção da feature foi evidente, 74.1% votaram a favor! Tiveram outras perguntas na pesquisa também, você pode ver o resultado completo aqui. E você, o que achou dessa proposta? Vale lembrar que ela ainda pode ou não ser aprovada, tudo depende da Oracle. Mas se você quiser, já dá pra ir brincando com alguns builds não oficiais.

Se você ainda não domina Java 8 e os recursos atuais da linguagem, com certeza vai gostar do nosso curso do Alura e também do livro publicado pela Casa do Código. O Alura usa Java 8 desde o lançamento final desta versão da linguagem, e com Java 9 não será diferente.


  • Não gostei.

    • Rodrigo Turini

      Oi Edwar.
      Qualquer avanço na inferência de tipos é bem-vindo, mas a sintaxe realmente pode causar essa sensação.
      Qual os downsides que você vê na feature?

      • Jair

        Também achei pouco útil. Qual a vantagem de não escrever o tipo, mas precisar escrever “var”? Ou, invés de usar “final” usar “val”.

        Algo assim poderia ser mais útil:

        nome = “Fulano”;
        final numero = 123;

        • Rodrigo Turini

          Oi Jair. A vantagem é conseguirmos diferenciar uma nova variável de uma atribuição de valor em uma variável que já existe. No seu exemplo:

          nome = “Fulano”;
          

          nome foi criado agora, ou já existe e você está adicionando/sobrescrevendo seu valor?

          • Jair

            Exatamente por isso acaba tendo pouca utilidade, pois adiciona mais complexidade à linguagem, sem nenhum ganho real.

            Afinal, temos que pensar que além do compilador, o programador também tem que inferir o tipo. A leitura do código fica muito mais difícil, e tudo pra economizar alguns caracteres. String nome vs var nome é uma diferença de apenas 3 caracteres. Simplesmente não vale a pena. Principalmente pelo tipo não ser dinâmico.

  • Vanderson

    Cara, não gostei disso. Java ta começando a perder sua essência que fez com que eu me apaixonasse por essa linguagem. Logo, logo ela vai estar igual javascript.
    Não acho legal ter que olhar o valor que uma variável recebe para saber o seu tipo, prefiro mil vezes olhar o TIPO DECLARADO nela. Vejo que com essa mudança pensaram bastante em quem escreve o código, mas a manutenção do mesmo é que leva mais tempo. Não quero dar manutenção em um código que esteja cheio de “vars” e “vals” por todos os lados. Coisa de gente preguiçosa, não sei por que reclamam tanto, se não querem escrever muito basta saberem usar o “ctrl + espaço” do eclipse ou qualquer IDE decente.

    • Rodrigo Turini

      Oi Vanderson. Como eu disse no post, é uma proposta bastante polêmica.

      Um ponto que vale comentar é que você não vai poder usar var ou val como “tipo” em parâmetros de método, retorno ou qualquer variação dessas. É apenas no momento de declarar, então não torna tão grave pra legibilidade e logo, manutenibilidade.

      Quer um exemplo? Imagina que por algum motivo você precise guardar um stream do entryset de um mapa em uma variável local, pra decidir o tipo de operações aplicar no decorrer do código ou em algum tipo de strategy. Atualmente ficaria assim:

      final Stream&lt;Map.Entry&lt;Integer, String&gt;&gt; stream = map.entrySet().stream();
      

      O tipo:

      final Stream&lt;Map.Entry&lt;Integer, String&gt;&gt;
      

      é realmente relevante pra legibilidade e manutenibilidade? Nem sempre.

      Com a sintaxe proposta ficaria assim:

      val stream = map.entrySet().stream();
      

      Repara que em nenhum dos dois casos esse código é legível ou bom de manutenção. Aí que entra a idéia de sempre escrever oq chamam de human readable code. Tem que ficar legível pra você e não pra máquina. Veja a diferença que um rename faz:

      val stream = idAndNameMap.entrySet().stream();
      

      Agora eu sei que o mapa é de ids e nomes, ids são números, nomes são strings, o código fica legível e não preciso processar muita informação pra entendê-lo.

      Enfim, usar ou não os recursos novos é uma decisão que precisa ser tomada em time, levando em consideração todos os prós e contras para sua equipe, no caso a caso. Não existe uma solução global que funcione perfeitamente para todos, tenho certeza que muitos ainda vão preferir ler a declaração do final stream com entry de strings.

      • Vanderson

        Ótimo exemplo! Realmente agradar gregos e troianos é impossível. Tendo uma pessoa na equipe que manja mesmo dos paranaues e que realmente saiba os prós e contras de algumas novidades do java ajuda muito na hora de decidir se devemos usa-la ou não.

        Se a gente trabalhasse na mesma equipe e você me mostrasse essa vantagem de usar a inferência eu iria me calar na hora rs…acho que essa explicação que me deu agora cabe no post em, vai abrir a mente de muitas pessoas quanto essa novidade rs…

        Obrigado!

        • Rodrigo Turini

          Obrigado pela sugestão, Vanderson! Vou tentar encaixar no post depois.
          Nosso mundo é bem pequeno, que sabe ainda não trabalhamos no mesmo time 🙂
          um abraço

          • Vanderson

            ahah..quem sabe! Quando eu estava começando tentei entrar ai como estagiário mas não consegui. Tenho vontade de um dia virar instrutor….quando conseguir um tempo pra me dedicar eu bato na por da Caelum, vai que….rs…

  • Geison

    Para mim é bastante positiva essa “reformulação” que tem acontecido na linguagem. Java é uma linguagem muito verbosa que acaba nos fazendo escrever mais do que devemos. Dependemos sempre das IDE’s para contornar esse problema. Desafio alguém aqui programar Java com Vi puro! 😀

  • Geison

    Turini, uma pergunta: que IDE você utiliza para realizar os testes nessa versão da JDK?

    • Rodrigo Turini

      VIM 🙂

      As IDEs ainda não estão (nem deveriam estar) muito preparadas pro Java 9.
      Dá pra usar qualquer editor e compilar com o bom e velho javac.

      • Geison

        Monstro! kkk
        Você utiliza algum plugin para lhe auxiliar? Se sim, quais?

        • Rodrigo Turini

          hahahahaha

          Eu sei que existem vários, mas nunca senti necessidade de usar nenhum.

          Uma coisa que eu faço bastante é criar aliases no terminal pra facilitar o processo de compilação, principalmente quando estou usando jigsaw. Em breve vou escrever um post sobre ele também.

  • É incrível como o Java está copiando coisas do Scala. Eu acho inferência de tipos algo fenomenal. Suja bem menos o código e não aumenta nem um pouco a complexidade do código.

  • Pingback: Java 9 na prática: melhorias na API de Collections e mapa | blog.caelum.com.br()

  • TypedScript aceitando interfaces, classes abstratas, herança… ES6 também aceitando bastante coisas novas parecidas com java, agora o java aceitando var e val, será que estamos caminhando para uma linguagem universal ?

    Abraçoooos

Próximo ArtigoReduzindo de N ifs para nenhum com Strategy em Java