Olhando o panorama java neste século (depois de 2001) tivemos dois grandes momentos na sua história. Em 2002 é lançado o java 1.4 ( hoje java 4) e o lançamento do java 5 em 2004 . A versão4 é importante não apenas pelo conjunto de coisas que adicionou à API (NIO, por exemplo) e à linguagem (assertions) mas porque foi usada por muito tempo estabelecendo um padrão de fato. Só no final de 2004 que saiu o java 5. Que só começou a ganhar tração em 2005. Foram mais ou menos 3 anos de hiato. Em consequência o java 5 adicionou um conjunto enorme de funcionalidades (enum, annotations, o pacote java.util.concurrent, generics). Por causa da diferença demorou para migar o código do 1.4 para 1.5. Com o advento do java 6 em finais de 2006 (apenas dois anos depois) alimentando-se das inovações do java 5 e adicionando melhores API, parecia que iriamos entrar num padrão de cronograma. E aliás a Sun planejava isso. Liberações a cada 2 anos onde as versões impares seriam inovações da linguagem e/ou da JVM e as pares seriam inovações da API. Isto nos daria atualizações a cada dois anos com inovações a cada 4 anos. Era um bom cronograma.
Mas ai chegou a Oracle e comprou a Sun. As cabeças mais pensantes foram embora. Alguns para o Google , alguns para a Microsoft e outros se espalharam pelo mundo. A concorrência ganhou bastante. Não é por acaso que o Google tem o Android e a Microsoft continua lançando versões de C#, que, na opinião de muitos, superou a linguagem java. A Oracle pisou muito na bola. Parece até que nem é uma empresa de tecnologia. O problema é que a Oracle estava de olho no java embarcado (especialmente o javacard que existe em cada cartão de credito/débito do mundo. O javacard era a única fonte de renda para o Sun que vinha do java).A Oracle tinha alguma experiência com o Java EE e até tinha a sua VM. Mas uma coisa é implementar especificações e participar da JCP, outra coisa é ser o mentor da JCP. A Oracle começou criando o JDK 7 como uma versão própria sem que ninguém desse pitaco. A comunidade se revoltou. A Oracle teve a decência e a humildade de ouvir. Repensou. Voltou ao JCP. Mas era tarde de mais. Muito tempo foi gasto.
O problema não seria tão grande se a Oracle não tivesse comprado a Sun no momento em que o fez. A Sun estava atirando para todos os lados – e com razão na época. O JavaFX era a bola da vez, muito mais importante que o resto do java. Era o caçula. E a importância era estratégia para lutar contra o Silverlight e o Flash. Essa guerra foi perdida pela Sun, mas ganha pelo HTML 5 , o JQuery e a Mozila que tornaram obsoletos esse conceitos. O JavaFx passou de uma linguagem diferente que rodava na VM ( como Scala ou Groovy) para ser parte integrante da API java SE. O JavaFX 2 é isso. Mas toda esta atenção dada ao FX deixou o java padrão e a JVM orfã. Tanto que a prioridade era o FX como na prática pessoas foram remanejadas de outros projetos para ajudar nesse.
O fato do objetivo do Java SE 7 ser ambicioso também não ajudou. Não apenas a introdução de Closures , mas outras coisas como o update do suporte a Unicode, o projeto Coin , O novo bytecode InvokeDynamic, a nova API de files , a nova API fork/join e a nova API de datas e tempos. Era muita coisas nova ao mesmo tempo. Se a oracle seguisse o cronograma da Sun, a versão 7 teria apenas coisas como Closures e o Projeto Coin. Nada de API novas. A versão impar traria o sucesso das mudanças na linguagem como a 5 fez antes dela, e a 8 traria novas API como a 6. Mas na boa verdade a divisão não é tão simples e mesmo na 5 houve uma serie de API novas como a concurrent utils.
Todo este rolo resultou na Versão 7 ser partida em duas. A 7 com o que já estava pronto e que poderia ser utilizado pela comunidade. Não veio closures, mas veio a nova API de file e o Invoke dynamic. Bom para quem faz servidores de aplicação ou quer criar sua linguagem em cima da vm java. E o mais complexo ficou para a 8, na promessa que seria logo a seguir. A versão 7 saiu em 2011 com a promessa da versão 8 para 2012. Mas já foi dito que a 8 sairá apenas em 2013, mas final de 2013 ( provavelmente setembro). O java 6 saiu em 2006 e o java 7 em 2011 , são 5 anos de diferença para uma versão que só será usada por quem está escrevendo código muito dependente de I/O ou está criando a próxima linguagem da VM. Para o desenvolvedor comum, não adiantou muito. Para não dizer nada.
Então o que a versão 8 nos trará que justifica todo este tempo de espera ? Será isso bom ou ruim ? Estamos pegando o trem do C# ? Scala é uma opção ?
A coisa que todo o mundo fala que justifica isto é o novo conceito de Lambdas. Mas na realidade isso é só uma parte do problema. Existem muito mais features na versão 8 que são tão ou mais importantes que os lambdas – como a API de datas e tempos – que não estão sob a luz dos holofotes. Lambdas é importante ? Com certeza. Programação funcional é necessária para otimizar o uso das máquinas multi-nucleo que temos hoje (e teremos no futuro). Mas o Java sempre permitiu programação funcional. O problema é apenas a sintaxe. O compilador não ajuda a quem quer programar dessa forma.
O futuro é multicore. Não é apenas criar novas threads, é maximizar o tempo para cada uma usando núcleos diferentes. Multithread em um core cria concorrência, mas não necessariamente velocidade e tem mais que ver com assincronismo do que propriamente com velocidade. Para continuar na corrida o Java precisa capturar o poder de usar multiplos nucleos de forma otimizada. O Fork/Join da versão 7 nos dá uma forma de fazer isto, mas não é simples. É fork/join é um framework genérico – como todos em java – e não foi pensado apenas para fazer uma única coisa. Mas os programadores nunca usarão este framework se tiverem que raciocinar toda a sua dificuldade. Uma forma mais inteligente é encapsular o uso do Fork/Join em outras classes de forma que o código seja o melhor possível, e único (principio DRY). A aposta do java 8 é o bom e velho conceito de stream agora aplicado a coleções. A ideia é : se vc tem um Iterabel, vc tem o poder de aproveitar tanto o paralelismo como a programação funcional. A mudança na sintaxe do java para incluir o que se chamada de Lambdas é apenas um arranjo estético para o programador escrever menos. A funcionalidade em si não é muito diferente das classes anônimas aninhadas.
A ideia geral é que os métodos também possam ser objetos. Possam ser colocados em variáveis e passados como parametros. Isto é muito importante em programação funcional porque é comum que uma função retorne uma função em vez de um valor. Mas o java 8 não nos trará programação funcional completamente. Não será possivel declarar um método como recebendo um lambda. Ainda apenas poderemos receber tipos comuns ( classes, interfaces, enum). Não existirão literais de função ou método, ou seja, um método não poderá ser declarado no nada, sempre terá que estar associado a um tipo. Do lado de quem cria um método pouco mudará. Mas do lado de quem invoca o método, poderá ser usada uma sintaxe mais simples (menos bla-bla-bla) para passar o argumento. Especialmente quando o argumento é um Objecto de Função (padrão Functor). Objetos de função são comuns em java. Todos conhecemos Comparator, Runnable, Callable, e tantos outros. São interfaces que definem apenas um método. Estes objetos de função ( ou, SAM types – tipos Single Abstract Method, como a Oracle está chamando) é o que realmente teremos no java 8. Suporte a criar implementações destes objetos de forma mais simples usando uma nova sintaxe. Porque todos os lambdas deverá em algum momento corresponder a um objeto de função a assinatura do método em si está na interface do tipo, o programador apenas terá que prover a implementação.
A sintaxe definitiva ainda está sujeita a alterações, mas será bem semelhante à usada em Scala e C#.
(lista de argumentos) -> { código de implementação }
As chaves podem não ser usadas se usarmos apenas uma expressão. Então Runnable seria assim
Runnable r = () -> {// faça algo};
E Comparator seria
Comparator<T> comparator = (T a , T b) -> { // compare a com b e retorne um inteiro}
Esta nova forma de escrever objetos de função é útil para diminuir a escrita, mas não possibilita nada que não fosse possível antes com inner classes. O que realmente é diferente é a possibilidade de usar métodos que já existem em classes como se fosse objetos de função. Aqui a relação é mais livre, se o método tem os mesmos parâmetros e retorno que o método na interface que define o objeto de função será possível simplesmente usá-lo. Por exemplo, se você tem um método estático na classe X com a assinatura compare(X a, X b) você poderá criar um Comparator diretamente desse método , assim :
Comparator<X> comparator =X::compare;
Isto pode ser útil, mas poderá ser também largamente abusado. Por exemplo, o programador cria um método compare na classe Produto, em vez de criar a classe ProdutoComparator.
Estes arranjos na sintaxe nos farão aumentar o uso de Lambdas (objetos de função). Um dos lugares onde é óbvio usar isto é na API de coleções. A API de coleções da Apache (Commons Collections) usa esta ideia ha anos. O problema é que para isso tiveram que criar suas próprias interfaces de coleções que não são totalmente compatíveis com a API padrão. Então, o desafio no java 8 não é incluir lambdas – isso é um desafio para o pessoal de compiladores e jvm, mas não para o programador. O desafio é como estender as interfaces de coleções que já existem. A Oracle poderia simplesmente adicionar métodos novos nas interfaces, mas isso levaria um monte de gente que implementou essas interfaces a ser forçado a implementar esses novos métodos. Embora possível, isso não é prático. Então foi imaginado uma funcionalidade nova chamada Extention Methods. Este nome é enganoso porque não é a mesmas coisa que em outras linguagens. Na realidade o java 8 nos trará a opção de criar Default Methods, ou seja, implementações default para métodos em interfaces. Isto irá criar um efeito nas interfaces semelhantes ao de uma classe abstrata – poderemos ter implementações em interfaces. Isto é verdade, mas estes métodos default não podem ser final e sempre são públicos. Isso torna as coisas um pouco diferentes de uma classe abstrata. Com isto , finalmente poderemos ter o método sort() na interface List.
Mas é preciso ter noção de que este mecanismo é uma forma de extensão para quem definiu a interface, não para quem a usa. Ou seja, você poderá usar este mecanismo nas interfaces que você criou na sua aplicação, mas não poderá adicionar métodos “a vulso” em interfaces de outrem. Ou seja, a Oracle poderá incluir métodos na interface List, mas você não. Então este mecanismo é o primo pobre do mecanismo verdadeiro de Extension Methods – que permitiria associar métodos novos a qualquer interface ou classe – como é em C# ou Scala (embora em scala seja algo ainda melhor que em C#, conhecido como padrão Pimp My Library)
A pergunta então é : porque não migrar logo para Scala ? Afinal ela oferece muito mais do que simples lambdas e conversões para objetos de função. Ela oferece verdadeira programação funcional.
A resposta é meramente politica. Se você pode mudar para Scala, faça-o. Se você não pode, então as vantagens sintáticas do java 8 serão bem vindas. Repare que, como falei, a programação funcional em java sempre foi possível, o problema é que o compilador não ajuda(va).
Propostas mais avançadas para incluir verdadeira programação funcional, inclusive controle de exceção (lambdas só usam exceções não verificadas) e até a possibilidade de criar estruturas de comando como seu próprio if ou try (como é possível em scala) estiveram na mesa. Mas devido ao atraso da versão 7 que virou 8 muitas coisas foram cortadas.
A nova sintaxe não irá obrigar o programador java a ter que aprender um monte de coisa nova como na versão 5, mas irá dar aos criadores de API uma forma de escrever suas API de forma mais amigável ao uso de multicore. Isto por consequência obrigará os programadores a usarem a nova sintaxe para usar as novas APIs. Um exemplo disto é a API de fork/join. A API em si é escrita com o uso das boas e velhas interfaces, mas o estilo de programação da API é funcional. Ou seja, ela espera que lhe sejam passados objetos de função que a API irá chamar e compor conforme achar necessário. Ela espera que o programador crie objetos de função. Hoje , no java 7, ainda temos usar classes anônimas para isto, mas no java 8 a mesma API será usada com lambdas. Isso tornará a vida do programador mais fácil e por consequência a API será mais usada, pois será mais fácil.
Embora o desenho das APIs não dependa da existência de lambdas , o sucesso da API sim depende disso. O problema é que a Oracle não teve a mesma ousadia que a Sun teve o java 5 e não nos proveu com um mecanismo realmente novo. Como eu disse, por baixo dos panos a complicação é muito maior do que parece, o compilador e a JVM têm muito que fazer aqui, mas para o programador a inclusão de lambdas não irá causar revoluções, como foi a introdução de generics, por exemplo.
Em resumo, as adições do java 8 são interessantes e uteis do ponto de vista de quem quer criar e usar API orientadas a funções em java sem o peso da sintaxe de classes anônimas, mas pelo tempo que demorou poderíamos estar recebendo muito mais. Coisas que são comuns em Scala ou mesmo em Groovy ha anos, e que foram ignoradas. Isto afeta a competitividade da linguagem java face a outras linguagens e até linguagens de outras plataformas ,como C#, e em vez de vermos pessoas migrando de java para scala ou groovy, veremos pessoas migrando para C#. Scala realmente corre atrás e consegue bater o C# mano-a-mano e com implementações mais bem pensadas. Por exemplo, em scala é possível criar algo como o LINQ, porque a linguagem permite transformar um encadeamento de chamadas em uma arvore abstrata que o programador – e não apenas o compilador – pode usar. Ou seja, a mágica que o LINQ faz, mas que só ele faz, em scala pode ser usada para criar qualquer DSL que você quiser.
Esperemos que após o java 8 o cronograma de Oracle estabilize e comecemos a ver coisas mais arrojadas. Mas o fato é que hoje em dia, a inovação não vem pela linguagem java e sim por outras linguagens da JVM. A JVM é excelente e todos sabem disso, e por isso cada vez mais linguagens migram seu runtime para a jvm, mas nenhuma delas é mainstream end-to-end, de uso genérico, etc , etc… e aceite, como java. E não faltam candidatos ( Fantom, Gosu, Scala, Kotlin para citar algumas), o problema é a aceitação. A evolução da linguagem java é muito mais importante do que parece pois neste momento não é viável pensar na JVM sem ela. Embora possível ninguém quer assumir esse risco.
Se nada mudar, o lançamento do java 8 daqui a mais ou menos um ano, será um marco na história no java, mas o seu efeito não será tão revolucionário como foi o java 5. A mudança é mais subtil e só veremos o seu efeito daqui ha alguns anos quando as API e frameworks começarem a tirar mais partido da programação orientada a objetos de função afim de tirar partido do multicore e do uso embutido do framework de fork/join.
Excelente explanação! Técnica e detalhada, porém de fácil entendimento. Valeu!
[…] mais de ano falei sobre o que o java 8 ia trazer. Finalmente ele chegou. E agora? Valeu a pena esperar? Em uma palavra: sim. Em mais palavras: nem […]