artigo A Evolução da Linguagem Java Veja as mudanças da linguagem para o Java 7 e quais são as possíveis novidades para o futuro Roberto Perillo ([email protected]) é bacharel em Ciência da Computação e está atualmente concluindo o curso de especialização em Engenharia de Software do ITA. Trabalha com Java há quase 5 anos, possui as certificações SCJP, SCWCD e SCJD, e participa ativamente dos fóruns do JavaRanch. Já trabalhou com JEE em grandes empresas, como Avaya e IBM, nesta última foi co-fundador do time de inovação de ibm.com GWPS Brasil. Eduardo Guerra ([email protected]) é pesquisador em desenvolvimento de frameworks, participando de projetos opensource como SwingBean, Esfinge Framework, ClassMock e JColtrane. Atualmente está cursando doutorado no ITA, onde também já concluiu graduação em Engenharia da Computação e mestrado. Possui as certificações SCJA, SCJP, SCWCD, SCBCD (1.3 e 5.0), SCJWSD, SCMAD e SCEA e experiência como arquiteto de software nas plataformas Java SE, Java EE e Java ME. Atua também como professor na graduação do ITA e nos cursos de pós-graduação ITA/Stefanini. A linguagem Java é hoje uma das mais utilizadas em todo mundo. Em 2006, as estatísticas do Sourceforge apontavam que ela havia ultrapassado a linguagem C++ em número de projetos. Segundo o site TIOBE Software, que divulga um índice mensal a respeito da popularidade de linguagens de programação, em julho de 2009, Java é a linguagem mais popular entre os desenvolvedores. Esse índice se baseia no número de profissionais qualificados, cursos e serviços oferecidos no mercado, e também nas procuras realizadas em sites, como Google, MSN, Yahoo!, Wikipedia e YouTube. Analisando o histórico da linguagem neste índice, disponível desde 2001, ela se posicionou sempre entre a primeira e a segunda posição. Imagine agora como deve ser difícil realizar alterações em uma linguagem como Java. Cada mudança poderá afetar e ter um grande impacto sobre um número imenso de projetos e desenvolvedores em todo 36 www.mundoj.com.br mundo, e por isso mesmo cada uma deve ser realizada com cuidado. Por exemplo, no Java 5, em que funcionalidades de linguagem foram acrescentadas, diversos programas não compilaram na nova versão devido a adição da palavra reservada enum, que muitas vezes era utilizada em identificadores, como para nomes de variáveis. Neste artigo, é apresentada com exclusividade uma entrevista realizada com Alex Buckley, o atual responsável na Sun pela linguagem Java e pela arquitetura da máquina virtual. Nesta entrevista, ele passa um pouco de sua enorme experiência na evolução da linguagem e dá algumas dicas a respeito do que podemos esperar de novidade no futuro da linguagem e da plataforma. O artigo também irá explorar mais a fundo as funcionalidades de linguagem previstas para entrarem no Java 7 e faz algumas previsões a respeito do que se pode esperar para outras futuras versões. "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB Entrevista com Alex Buckley 'JHVSB&EJUPSDIFGFEB.VOEPKFNTVBDPOWFSTBDPN"MFY#VDLMFZ 'JHVSB&EJUPSDIFGFEB.VOEPKFNTVBDPOWFSTBDPN"MFY#VDLMFZ .VOEPK – Por favor, descreva sua posição e suas responsabilidades na Sun Microsystems, Inc. "MFY#VDLMFZ – Meu nome é Alex Buckley e sou o “teologista computacional” da linguagem Java e da máquina virtual Java (JVM). Dia a dia, isto significa que sou responsável pela especificação da linguagem Java e pela especificação da JVM. Estas definem, de uma forma independente de implementação, o significado da linguagem Java e da JVM. Eu não me preocupo com o comportamento do “javac”, ou qualquer das bibliotecas de classes, por exemplo. Eu só me preocupo com a definição da linguagem Java em si, o que ela faz e por que ela o faz de tal forma; e o mesmo para a máquina virtual. Obviamente, existem grandes sobreposições entre o design da linguagem e a máquina virtual, então manter as duas especificações sincronizadas tem sido historicamente grande parte do trabalho. Com mais linguagens começando a serem executadas na JVM, as especificações podem começar a ir em diferentes direções. .VOEPK – Que tipo de coisas você busca em conferências acadêmicas como o ECOOP (European Conference on Object-Oriented Programming) e o OOPSLA (International Conference on ObjectOriented Programming, Systems, Languages, and Applications)? "MFY #VDLMFZ – Nos dias de hoje, a orientação a objetos tem um papel muito importante na indústria. Outros paradigmas de programação, como a programação funcional ou lógica, têm seus seguidores, porém a orientação a objetos é o paradigma predominante. Congressos como o ECOOP e o OOPSLA apresentam muitos trabalhos baseados em linguagens predominantes na indústria, como Java ou C#. O que eu procuro nestes congressos são artigos que descrevam o núcleo de uma característica de linguagem. Não é um problema se esta característica não se pareça com Java, ou se é expressa em uma linguagem de brincadeira, ou até mesmo se é expressa formalmente em uma linguagem matemática. Eu não preciso saber que “esta é uma extensão Java e Todos os anos, vários eventos sobre programação orientada a objetos são realizados ao redor do mundo, onde são apresentados diversos trabalhos focados ou baseados na linguagem Java. Particularmente, no meio científico, os que mais se destacam são o ECOOP (European Conference on Object Oriented Programming) que ocorre na Europa e o OOPSLA (International Conference on Object-Oriented Programming, Systems, Languages, and Applications) que acontece nos Estados Unidos. No OOPSLA do ano passado, mais especificamente no dia 22 de outubro, a revista Mundoj teve oportunidade de realizar uma entrevista exclusiva com Alexander Buckley, que é o atual responsável pelas especificações da linguagem Java e pela arquitetura da JVM. Nesta conversa, foi possível obter várias informações interessantes, como fatos ocorridos há mais de 10 anos que fazem parte da história da linguagem e indícios do que ainda está para entrar nela. Foi possível também ter noção do quão difícil é evoluir uma linguagem quando ela já está sendo amplamente utilizada na indústria. a ideia na linguagem seria de tal forma”, porque na prática, como as características são em linguagens reais é um tópico muito complicado. Por exemplo, você não pode inventar suas próprias palavras-chave porque isto quebraria código antigo. Eu procuro em artigos publicados no ECOOP e no OOPSLA pelo núcleo de uma ideia que possa ser adicionada à linguagem, então como elas são expressas não é um problema. Que tipos de características são interessantes? Bem, eu diria características de linguagem que suportam melhor modularidade de programas, assim como características de linguagem que suportam melhor análise de programas. Muitas pessoas não sabem, ou até saibam, mas não percebam o quão profundo isto é. A linguagem Java é quase única pelo fato de que a especificação da linguagem Java requer um compilador para executar alguns tipos de análise de programa. Na análise de alcance, não se pode ter declarações que nunca serão executadas. Na análise de declaração, deve-se sempre atribuir valores a variáveis finais e locais antes de usá-las. Isto na verdade é mandatório na especificação da linguagem Java. Se houver uma análise de programa útil que nós acharmos que vá beneficiar todos os desenvolvedores Java, há uma grande possibilidade de colocá-la nos padrões. .VOEPK – Há muitas boas ideias sendo apresentadas nestes congressos. Quanto tempo leva para uma boa ideia ir do meio acadêmico para a indústria? "MFY#VDLMFZ – Um enorme exemplo de uma característica proposta no meio acadêmico que foi adicionada à linguagem é, claro, generics. Assim que o Java foi lançado, o meio acadêmico ficou muito animado. Philip Wadler e Martin Odersky começaram a pensar como deveriam ser os tipos genéricos em Java. Como deveria ser a implementação? Teríamos que mudar só a linguagem ou a JVM também? Isto foi no final de 1996, quase tão logo o Java foi lançado. 37 "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB Os generics foram algo em que o meu antecessor se envolveu muito. Isto foi na JSR 14, em 1998. Martin Odersky reescreveu o compilador Java, que se chamava GJ na época, para suportar generics, e aquela implementação dele se tornou o “javac”. Então o “javac” 1.3 era o compilador do Martin. Claro, os generics foram novamente estendidos para suportar os wildcards, e isto foi novamente o resultado de um trabalho acadêmico, de Mirko Viroli e Atsushi Igarashi, sobre variâncias, que são representadas na linguagem Java como “?”, “? extends” e “? super”. Este é um caso interessante no qual a pesquisa havia começado há anos, e assim que começamos a tentar aplicar o conceito a programas reais e a bibliotecas Java, descobrimos que o trabalho acadêmico não havia ido tão longe e precisa de mais desenvolvimento. Assim surgiram os wildcards. .VOEPK – O oposto também acontece? Por exemplo, a indústria começa a usar algo e o meio acadêmico percebe que precisa pesquisar o assunto? "MFY#VDLMFZ – Esta é uma pergunta interessante! No mundo do Java, muita inovação vem da implementação dos padrões ao invés dos próprios padrões. O grande objetivo do Java SE, Java ME e do Java EE é que existam padrões, e se você escreve seu código de acordo com estes padrões, você pode usar as bibliotecas e APIs destes padrões. O código vai funcionar com qualquer implementação, então muita inovação está em fazer implementações realmente eficientes ou fazê-las rodar em várias plataformas ou dispositivos bem pequenos. Os padrões evoluem, por natureza, de forma mais lenta do que a implementação, que pode usar vários truques. .VOEPK – Isso aconteceu com anotações, talvez? "MFY#VDLMFZ – Anotações na prática são populares e certamente habilitam muitos trabalhos acadêmicos, porque esses trabalhos precisam de metadados. Metadado não é um conceito muito complexo, e as anotações da linguagem Java são bem simples. O que os acadêmicos fazem com as anotações e o que é representado com as anotações é muito interessante. Eu diria que há funcionalidades na linguagem que suportam mais pesquisas acadêmicas, como o uso acadêmico das anotações. E a plataforma em si, tornando-se open-source com o JDK aberto, permite que as pessoas peguem o compilador Java, a JVM, o compilador Just-inTime e façam experimentos neles. .VOEPK – O quão difícil é evoluir uma linguagem? Podemos comparar esta evolução com a evolução de uma API, por exemplo? "MFY #VDLMFZ – Existem alguns aspectos da linguagem que podem ser evoluídos. Existe a sintaxe da linguagem, o sistema de tipos da linguagem e o comportamento das instruções da linguagem. Muitas pessoas perguntam: “É possível adicionar uma palavra-chave para fazer isso ou aquilo?”, mas é bastante complicado mudar a sintaxe ou a gramática da linguagem Java. Não é possível simplesmente adicionar novas palavras-chave, pois qualquer palavrachave que você possa imaginar, alguém em algum lugar a estará usando como nome de variável. No passado, pudemos verificar que no momento em que uma nova palavra-chave é adicionada, alguns programas são quebrados, e não 38 www.mundoj.com.br compilarão na última versão da linguagem Java. É muito mais difícil evoluir a sintaxe da linguagem do que as pessoas imaginam. É como evoluir a sintaxe da linguagem se você adiciona palavras-chave que só agem como palavras-chave quando for preciso, e em todos os outros lugares do programa, elas agem como identificadores. Então, se elas agem como nome de variável, ou nome de classe, ou nome de método hoje, elas continuarão agindo amanhã. Estas palavras-chave agem como palavras-chave somente em locais bem específicos, talvez no topo do arquivo-fonte, em que precisa ser uma palavra-chave. Realmente, é possível evoluir a sintaxe da linguagem, e aprendemos uma lição ao adicionar “enum” do Java 1.4 para o Java 1.5, que causou verdadeiras dores de cabeça a muitas pessoas. Aprendemos uma lição e não cometeremos este erro novamente. Não vamos, no futuro, adicionar palavras-chave que quebram o código-fonte. Evoluir a sintaxe é complicado e aprendemos como fazê-lo melhor. Evoluir o sistema de tipos da linguagem, e muitas propostas acadêmicas estão nesta área, é a coisa mais complicada. É muito difícil mudar o sistema de tipos Java, porque o sistema de tipos da linguagem Java (a) é refletido no sistema de tipos da JVM; (b) é embutido nas bibliotecas de reflexão, então seria necessário mudá-las também; e (c) novamente, sempre tem implicações em programas existentes, e a Sun não quer quebrar o código das pessoas só porque há algum novo tipo de sistemas mais elaborado. Mas é possível adicionar um novo tipo. O debate sobre closures foi efetivamente sobre se devemos adicionar funções à linguagem Java. A questão é se estaríamos adicionando algo tecnicamente seguro. Os generics sem os wildcards são seguros, mas muito restritos. Por isso, os wildcards foram adicionados, e há restrições nos wildcards que confundem as pessoas, mas que precisam existir, caso contrário você pode escrever um programa que compile, mas que trave em tempo de execução. Há algumas propostas que são aparentemente fáceis, mas que têm comportamentos complicados em tempo de execução. As pessoas dizem “tudo bem, não tem problema”, mas qualquer expert em sistemas de tipos irá lhe dizer que se você adiciona tipos estáticos, então o comportamento em tempo de execução deve ser previsível. É uma área muito complicada que tem um impacto enorme na plataforma e em programas. Assim, somos muito cautelosos ao fazer mudanças nessa área. .VOEPK – Em relação a aspectos, quando iremos vê-los nativamente na linguagem Java? Estamos perto ou longe disso? "MFY#VDLMFZ – Todos têm suas ferramentas, frameworks ou funcionalidades favoritas, que gostariam que fossem tiradas de uma API e fossem embutidas na linguagem Java. A dificuldade é que isto tornaria a linguagem Java mais difícil para todos aprenderem. Alguns gostariam de ver um framework orientado a aspectos adicionado à linguagem Java, outros gostariam de ver um framework orientado a recursos adicionado à linguagem Java. Uma linguagem não é nem uma aplicação nem uma API. Uma linguagem é melhor quando é menor e aplicações e APIs são melhores quando são maiores, pois assim podem ter mais poder e consequentemente terão mais usuários. Você só precisa se preocupar com as partes da API que você usa. Por outro lado, uma linguagem é melhor quando é menor, pois todo programador Java no mundo precisa entender efetivamente o significado de cada funcionalidade. "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB Então, se você faz dos joint points ou point cuts ou advices elementos de primeira classe na linguagem Java, você irá fazer duas coisas: primeiro, irá forçar todos os desenvolvedores Java a aprender aspectos. E mais, irá forçá-los a aprender aspectos como eles eram no dia em que você os embutiu na linguagem Java. Isto efetivamente pára novas pesquisas úteis sobre aspectos. Claro, existirão novos artigos sobre aspectos no futuro, mas uma vez que eles foram embutidos na linguagem, é a sua única chance de fazê-lo. Não dá para voltar e mudar depois disso. .VOEPK – É uma estratégia ter a JVM suportando outras linguagens, com outras funcionalidades, para que o Java não precise mudar tanto? "MFY#VDLMFZ – A JVM sempre foi capaz de executar outras linguagens-fonte, contanto que exista um compilador para elas. Não há nada na JVM que funcione somente para a linguagem Java. Podemos dizer que existem aproximadamente 100 linguagens-fonte que compilam para a JVM. Outra coisa a dizer é que muitas destas linguagens dinâmicas são mais velhas que o Java em si, como o Ruby, por exemplo. Então, não é surpresa que muitas linguagens além do Java têm sido executadas na JVM há muitos anos. Linguagens como Scala, que ainda estão em desenvolvimento, podem compilar para a JVM, então é possível comunicar-se com elas através de código Java, o que é muito interessante. É ótimo quando as pessoas olham para essas linguagens e expandem suas mentes, vendo novas formas de programar. Porém lembre-se de que estas linguagens estão livres para que seus designers removam funcionalidades a qualquer momento, ou redefinam funcionalidades. A Sun fica satisfeita quando pessoas escrevem outra linguagem que compila para a JVM e que tem o mesmo desempenho que código Java. A JVM é uma plataforma facilmente gerenciável, com muitas APIs. Tem uma ótima tradição quanto à segurança, e suas implementações atualmente têm ótimos compiladores Just-in-Time. Frequentemente, um compilador Just-in-Time na JVM consegue produzir código de máquina mais eficiente que um compilador C. E é interessante quando os criadores de outras linguagens também dizem que estão animados por verem suas linguagens tendo múl- /PWJEBEFTRVFEFWFNBQBSFDFSOP+BWB Após a adição de novas funcionalidades de linguagem no Java 5, houve uma grande discussão a respeito do que poderia aparecer nas novas versões. Apesar de algumas das funcionalidades nas seções seguintes estarem praticamente confirmadas no Java 7, a verdade é que nada é certo ainda. As funcionalidades que serão apresentadas dão uma noção de como será “a cara” da linguagem na sua próxima versão. Este artigo se limita a abordar as funcionalidades relacionadas com mudanças na linguagem, porém diversas outras mudanças, como, por exemplo, em APIs e na máquina virtual, também estão previstas. Uma que vale a pena ser comentada é o Projeto Jigsaw (que em português tiplas implementações, não somente um interpretador C nativo, mas também rodando na JVM, pois a JVM está em todos os lugares; em telefones celulares, em grandes servidores de aplicação. Então, todos ganham! .VOEPK – O que mais pode ser adicionado ao que você disse no começo, sobre a JVM e a linguagem Java indo em direções diferentes? "MFY #VDLMFZ – Posso dizer que o que está acontecendo na JVM para suportar linguagens dinâmicas é muito interessante. Acontece que, hoje em dia, é possível rodar JRuby, Jython e outras linguagens dinâmicas na JVM. Elas eram bem lentas, e hoje têm um bom desempenho. Agora a pergunta é se podemos fazê-las rodar tão rápido quanto se fossem código Java. E o que estamos fazendo é colocar ganchos na JVM, para que alguém, como o implementador JRuby ou Jython, possa instruir a JVM sobre como otimizar código compilado do JRuby ou do Jython. O código compilado Java não precisa usar estes ganchos, já que o compilador Java frequentemente efetua otimizações entre o código-fonte Java e os bytecodes Java. Os bytecodes Java, sua forma, seu tamanho, é de certa forma entendido nativamente por compiladores Just-in-Time. Mas ao rodar classes compiladas do JRuby ou Jython, o compilador Just-in-Time precisa de uma pequena ajuda para otimizar estes bytecodes. Está é a proposta da JSR 292. Dá até para imaginar o compilador Java também usando este mecanismo, sem mudanças na linguagem Java, somente como uma técnica de otimização. Também surge a ideia de que a linguagem Java em si poderia ser mais dinâmica. O que isto significa? Significa, por exemplo, que código Java poderia chamar métodos em código Ruby ou Python, compilados pelo JRuby ou Jython. Métodos Ruby e Python não possuem tipos em seus códigos-fonte. Atualmente, tudo no Java assume que o que está sendo chamado possui tipos. Achamos que provavelmente vai ser muito útil poder efetuar chamadas entre linguagens, principalmente quando estas outras linguagens estiverem rodando bem rápido na JVM e quando houver mais códigos escritos nestas linguagens rodando na JVM. seria algo como serra tico-tico), que visa a modularização da JDK 7. Este trabalho permitirá, por exemplo, que uma aplicação seja implantada apenas com os componentes da JDK que ela realmente precisar, o que poderá escalar a JDK para dispositivos menores e mais simples. Uma novidade interessante para os fãs das linguagens dinâmicas que também está prevista para a JDK 7 é um melhor suporte da máquina virtual para outras linguagens (ver última pergunta da entrevista com Alex Buckley). O objetivo é a criação de extensões para a máquina virtual que suportem a implementação de linguagens “não-java” em um nível de desempenho próximo da própria linguagem Java. As próximas subseções abordam as funcionalidades da linguagem que provavelmente irão "dar as caras" no Java 7. Serão abordadas as anota- 39 "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB ções de tipo e outras possíveis mudanças em anotações, o novo suporte a programação modular e outras pequenas melhorias de linguagens agrupadas no chamado Project Coin. t "OPUBÎÍPFNUJQPTEFBSSBZT // array de arrays de documentos somente leitura @ReadOnly Document [][] docs1; // array somente leitura de arrays de documentos Document @ReadOnly [][] docs2; // array de arrays somente leitura de documentos Document [] @ReadOnly [] docs3; t "OPUBÎÍPFNDPOWFSTÜFTEFUJQPT myStr = (@Readonly String) obj; "OPUBÎÍPFNSFTVMUBEPTSFDFCJEPTEFDPOTUSVUPSFT Person p = new @Immutable Person(); Anotações em Tipos A JSR 308, intitulada de “Type Annotations”, tem como objetivo estender o sistema de anotações existentes para que elas possam aparecer em qualquer local onde possa ser utilizado um tipo. No Java SE 6, é possível anotar declarações de classes, métodos, atributos e variáveis, e com essa adição, será possível no Java SE 7 que as anotações possam ser utilizadas para outros fins. A aplicação mais imediata desse novo mecanismo seria para o uso na JSR 305 “Annotations for Software Defect Detection”, que possui como objetivo a definição de anotações para verificações estáticas em tempo de compilação, visando a detecção de problemas que hoje acabam sendo detectados apenas em tempo de execução. Um exemplo seria a anotação @NonNull na declaração de variáveis, para as quais uma ferramenta faria uma verificação em tempo de compilação e acusaria um erro se existe a possibilidade da mesma assumir o valor nulo. Segundo o próprio documento que propõe a adição, o uso desse mecanismo de detecção de defeitos seria limitado sem as anotações de tipo, pois se perderia facilmente o rastro das variáveis anotadas quando fosse feito o uso de tipos genéricos, quando fosse feita a invocação de um método em um objeto, quando fosse efetuado um cast, quando uma classe fosse estendida etc. Para se ter uma ideia de como seria a sintaxe desse novo tipo de anotação, abaixo segue uma lista com alguns exemplos: t "OPUBÎÍPFNQBSÉNFUSPTHFOÏSJDPTQBSBDMBTTFTQBSBNFUSJ[BEBT List<@NonNull String> list; t "OPUBÎÍP FN QBSÉNFUSPT HFOÏSJDPT QBSB JOWPDBÎÜFT EF NÏUPEPT PV construtores obj.<@NonNull String>metodo(“string”); t t "OPUBÎÍPFNWFSJmDBÎÜFTEFUJQPTDPNPPQFSBEPSJOTUBODFPG if(str instanceof @NonNull String){ … } t "OPUBÎÍPFNSFGFSÐODJBTMJUFSBJTEFDMBTTFT Class<@NonNull String> c = @NonNull String.class; t "OPUBÎÍPFNBDFTTPBNFNCSPTFTUÈUJDPT @NonNull Type.field A primeira impressão que se tem quando do primeiro contato com a sintaxe desse novo tipo de anotações é que o código ficará extremamente confuso com a adição dessas novas informações. Imagine, por exemplo, a definição da variável abaixo: @Immutable Map<@NonNull String, @NonEmpty List<@Readonly Document>> files; Por esse motivo, é possível encontrar diversas opiniões em blogs na internet criticando essa nova feature da linguagem como os posts “When You Should Jump? JRS 308. That's When.” e “What Hath Java Wrought” que argumentam que o código pode ficar extremamente confuso e verboso com o uso desse novo tipo de anotação. Com a discussão iniciada, outros se posicionam a favor, como o post “JSR 308 Animosity”, que critica essa “animosidade” gerada a essa nova funcionalidade da linguagem. t "OPUBÎÍPQBSBSFTUSJÎÜFTEFUJQPTFQBSBXJMEDBSETFNUJQPTHFOÏSJDPT class BeanPresenter<E extends @Readonly Bean> { … } Collection <? super @Readonly Bean> col; t "OPUBÎÍPFNQBSÉNFUSPTEFUJQPT interface StaticList<@Readonly E> { … } t "OPUBÎÍPFNUJQPTJNQMFNFOUBEPTPVIFSEBEPT class UnmodifiafleList<T> implements @Readonly List<@Readonly T> {…} t "OPUBÎÍPFNDMÈVTVMBTUISPXTEFFYDFÎÜFT void createConnection() throws @Critical IOException { … } t "OPUBÎÍPOBTJOTUÉODJBTOBTRVBJTPTNÏUPEPTFTUÍPTFOEPJOWPDBEPT Na verdade, todo método de instância recebe um parâmetro implícito 'this'. Esta anotação é no tipo de 'this' e é colocada entre a lista de parâmetros e a cláusula 'throws'. public String toString() @ReadOnly { … } Apesar de podermos pensar que uma @NonNull String é um tipo que não pode receber atribuições de uma outra variável do tipo String (sem anotações) ou @Nullable String, a ideia é que essa verificação seja feita por plugins que irão atuar em tempo de compilação. Na arquitetura da máquina virtual, nada seria alterado no sistema de tipos da linguagem. t "OPUBÎÍPOPUJQPEFSFUPSOPEFVNDPOTUSVUPS class Statue { @Immutable Statue() { … } } Apesar da primeira aplicação pensada para essas anotações ser seu consumo estático em tempo de compilação, elas também poderão ser acessadas em tempo de execução. Como a API Reflection não dá acesso ao corpo dos métodos, em alguns tipos de uso essas anotações 40 www.mundoj.com.br Na verdade, essa nova funcionalidade é de uso opcional da mesma forma que o Generics, o que significa que ninguém será obrigado a utilizar essas anotações se não quiser. Ela permite apenas que novas metainformações possam ser adicionadas aos tipos, porém essa JSR não trata do significado ou semântica dessas anotações (o que fica, por exemplo, a cargo da JSR 305). Assim também como com a utilização de Generics, com o uso dessa funcionalidade pode-se fazer coisas muito interessantes, porém a codificação se torna mais difícil e o código mais poluído. "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB não poderão ser recuperadas em tempo de execução, como seu uso em variáveis locais, com o operador instanceof, em casts de tipos etc. Porém, em outros casos, como seu uso em geral na assinatura de métodos e na declaração de tipos e variáveis de classe, será possível a recuperação. A modificação que será realizada na API Reflection é bem pequena: basicamente a classe Type passará a implementar a interface Utilizando Type Annotations para problemas reais Para quem quiser experimentar as Type Annotations, existe uma ferramenta preliminar que permite o uso desse tipo de anotação. O Checker Framework (ver referências) é uma ferramenta que provê plug-ins para o javac, que verificam e previnem erros em tempo de compilação. Um desenvolvedor pode utilizar esse plug-in sem que seus companheiros de trabalho precisem utilizá-lo, bastando que eles não acionem os plug-ins no momento de executar o javac. O Checker Framework vem com tudo que você precisa, incluindo ferramentas que facilitam a criação de novos verificadores. As anotações que já são suportadas pelo Checker Framework podem ser consideradas como uma prévia das que virão na JSR 305 “Annotations for Software Defect Detection”. Esse tipo de verificação é uma ferramenta muito valiosa para arquitetos que desejam assegurar que determinadas propriedades no código sejam seguidas por todos os desenvolvedores em um projeto maior. Esse framework possui os seguintes tipos de verificação: t /VMMOFTT$IFDLFS este verificador através de anotações como @Nullable e @NonNull checa se em algum ponto do código existe a possibilidade de se invocar um método em uma referência nula. Esse tipo de prática evita que em tempo de execução seja lançada a indesejável e famosa NullPointerException. Existem anotações para opções mais avançadas, como @LazyNonNull, que permite que uma instância seja nula, porém não depois que ela for inicializada. t *OUFSOJOH $IFDLFS esse verificador irá procurar por usos incorretos do operador == e do método equals() para realizar a comparação de igualdade entre objetos. Interning, também conhecido como canonicalização, é um padrão de projeto no qual sempre que duas referências podem ser consideradas iguais, elas estarão apontando para a mesma instância. A anotação @Interned marca esse tipo de referência e evita que erros como o representado abaixo aconteça: Integer x = new Integer(23); Integer y = new Integer(23); Apesar da JSR 308 se chamar “Type Annotations”, ela também abrange em seu escopo outras possíveis modificações no sistema de anotações da linguagem Java. Na verdade, esse nome foi adotado para evitar um nome vago como “Extensions to Java’s Annotation System” e pelo fato da principal mudança ser a inclusão das anotações de tipo. A JSR 308 não possui a intenção de ser a última JSR sobre a definição de anotações na linguagem Java e é aceitável que sejam deixadas questões pendentes para serem consideradas em novas JSRs, que serão incorporadas em versões futuras da linguagem. Abaixo seguem algumas possíveis modificações no sistema de anotações AnnotatedElemment, que possui métodos que permitem a recuperação de anotações. O quadro “Utilizando Type Annotations para Problemas Reais” irá apresentar exemplos de aplicação desse tipo de anotação, para que os leitores possam avaliar os ganhos que se pode ter com essa adição em relação à complexidade que é adicionada na sintaxe da linguagem. System.out.println(x == y); // imprime false! t t .VUBCJMJUZ$IFDLFSeste verificador, através de anotações como @Mutable, @Immutable e @Readonly, procura erros que apontam locais que modificam indevidamente propriedades de objetos. O uso desse verificador evitaria, por exemplo, que uma lista ou seus objetos recuperados de um cache, fossem modificados externamente a ele de forma indevida, causando efeitos colaterais indesejados a outros clientes desse cache. -PDL$IFDLFS o objetivo desse verificador é prevenir alguns erros de concorrência. A anotação @GuardedBy deve ser utilizada em tipos de variáveis que só devem ser acessadas quando um determinado lock é obtido. A anotação @Holding já anota métodos que só devem ser chamados se um determinado lock foi obtido por quem o está chamando. O objetivo desse pequeno resumo não foi ensinar com detalhes como utilizar cada uma das anotações dos verificadores, mas fornecer uma visão geral do tipo de coisa que poderá ser implementada como esse novo recurso. O link para o manual do Checker Framework pode ser encontrado nas referências deste artigo. No material pesquisado, não foi encontrado nenhum exemplo referente à utilização das anotações de tipo em tempo de execução, porém analisando a API do EJB 3, por exemplo, é possível identificar alguns possíveis usos. A anotação @ApplicationException deve ser utilizada em classes de exceções, de runtime ou não, que devem ser encaradas como erros de negócio. Quando uma exceção desse tipo é lançada, só é dado o rollback da transação quando a anotação possui o atributo 'rollback = true'. Com esse tipo de abordagem, uma mesma classe de exceção não pode ser utilizada em situações em que o comportamento em relação ao rollback ou não das exceções seja diferente. Com o uso de anotações em tipos, um método que lança exceções poderia ter em sua própria assinatura, na declaração das exceções, a definição de quais delas devem fazer com que o container faça ou não o rollback da transação corrente. Obviamente, essa funcionalidade é apenas uma previsão, mas ilustra como uma nova gama de opções e possibilidades pode ser aberta com essa adição à linguagem. da linguagem. Algumas são bem suaves e contêm pequenas modificações que visam simplesmente simplificar a sintaxe, enquanto outras são modificações mais profundas e exigem que internamente grandes modificações sejam feitas. t "OPUBÎÜFTNÞMUJQMBTEPNFTNPUJQPna linguagem Java, não é permitido que uma mesma anotação seja utilizada duas vezes. Esse tipo de restrição faz com que seja necessária a criação de uma anotação com uma lista da anotação que se deseja repetir. Isso pode ser visto, por exemplo, na anotação @NamedQuery da API JPA que precisa ser encapsulada pela anotação @NamedQueries, conforme o exemplo abaixo: 41 "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB @NamedQueries({ @NamedQuery(name = "todasPessoas", query = "SELECT p FROM Pessoa p"), @NamedQuery(name = "pessoaNome", query = "SELECT p FROM Pessoa p WHERE p.nome = :nome") }) public class Pessoa { … } Com a implementação dessa modificação, será possível eliminar a anotação @NamedQueries e colocar várias anotações @NamedQuery diretamente na classe, conforme o código a seguir: @NamedQuery(name = "todasPessoas", query = "SELECT p FROM Pessoa p") @NamedQuery(name = "pessoaNome", query = "SELECT p FROM Pessoa p WHERE p.nome = :nome") public class Pessoa { … } t t /PWPTMPDBJTQBSBBOPUBÎÜFTtambém estão sendo estudados outros locais, além dos apresentados nas anotações de tipo, para a adição de anotações. Dentre eles estão anotações em comandos ou em um grupo de comandos, como loops e blocos. Os defensores dessa proposta argumentam que esse tipo de anotação traria benefícios para verificações de atomicidade e concorrência. Dentre as alterações necessárias estariam a definição da sintaxe e do formato do arquivo .class para armazenar essa informação. Dentre outras possibilidades também analisadas estão as anotações para expressões, para as quais ainda não se tem uma aplicação comprovada que justifique sua adição, e anotações para apenas certos tipos de comandos, que não apresenta muitos benefícios em relação à proposta mais geral. )FSBOÎB FOUSF BOPUBÎÜFT atualmente, uma anotação não pode herdar características de outras, o que dificulta a extensão de processadores de anotações, que devem atuar em um conjunto fixo de anotações. O recurso utilizado para contornar esse problema é meta-anotar uma anotação, para que o interpretador possa ler essa meta-anotação, como utilizado em frameworks como o Hibernate Validator e o JColtrane. A ausência de herança também limita bastante a expressividade das anotações. Considere uma anotação @PreCondition que recebe uma anotação @Expression com uma expressão a ser verificada: @PreCondition(@Expression(“a == b”)) public void method() { … } Sem herança, é impossível definir outra anotação que possa ser colocada no lugar de @Expression, como uma anotação que faz uma negação ou uma anotação que junta duas expressões como no exemplo abaixo: @PreCondition(@And(exp1 = @Expression(“a < b”), exp2 = @Expression(a < c)) public void method() { … } Existem duas soluções descritas na definição da JSR que estão sendo consideradas para a herança: a possibilidade de uma anotação estender outras anotações e a possibilidade de uma anotação estender e implementar interfaces. t "OPUBÎÜFTSFDVSTJWBTexistem algumas limitações na expres42 www.mundoj.com.br sividade das anotações, sendo exemplos o fato de uma anotação não poder receber uma anotação arbitrária como argumento e o fato de uma anotação não poder possuir uma definição recursiva. Esses tipos de estrutura já se provaram eficientes em outras tecnologias, como, por exemplo, XML e YAML. Para que isso seja possível, seria necessário para finalizar uma recursão outras adições, como a possibilidade de possuir argumentos nulos na anotação (que hoje também não é possível, somente valores default). t "SHVNFOUPTQPTJDJPOBJT a sintaxe das anotações quando elas recebem um argumento pode ser simplificada quando a mesma possui a propriedade value, o qual pode ser omitido. Exemplificando, no lugar de definir @Annotation(value = 13), é possível colocar apenas @Annotation(13). Infelizmente, quando é necessário definir mais de um argumento, essa simplificação não é possível e a sintaxe fica mais verbosa. Está sendo estudada uma forma de definir uma posição para os argumentos de forma que os nomes possam ser omitidos. Outra questão sendo abordada nesse tópico é a obrigatoriedade do nome value para a propriedade default, cujo nome pode ser omitido. Muitas vezes, esse nome não é significativo em relação ao domínio. A ideia é permitir que na definição da anotação seja definido, possivelmente através de uma anotação como @ValueMember, qual é o campo que será considerado caso o nome do argumento não seja definido explicitamente. t "OPUBÎÜFT FN IJFSBSRVJBT atualmente, é possível definir uma anotação que será herdada pelas subclasses de uma classe anotada através do uso da anotação @Inherited. Porém, essa herança é somente das anotações da superclasse e anotações definidas nas interfaces, por exemplo, não são herdadas. Poderia também ser interessante que anotações pudessem ser herdadas nos membros da classe, como em métodos sobrepostos e construtores. t "OPUBÎÜFT EFGBVMU diversas APIs possuem anotações de métodos que, ao serem definidas na classe, servem como uma definição default para os métodos da classe. Um exemplo seria a anotação @TransactionAttribute da especificação EJB 3. Porém, esse tipo de comportamento é implementado pelo componente que processa as anotações e não existe um mecanismo padrão para esse tipo de definição, que pode diminuir e simplificar o código. Uma possível mudança seria a definição de uma anotação para possibilitar esse tipo de definição, conforme o exemplo abaixo: @DefaultAnnotation(@MyAnnotation(“a”)) class Exemplo { … } Para que esse tipo de definição seja possível, também seria preciso que a funcionalidade que permite anotações possa possuir como argumentos anotações de tipos arbitrários. Como foi dito por Alex Buckley em sua entrevista, a introdução de anotações na linguagem Java permitiu o desenvolvimento de diversos trabalhos interessantes que utilizavam esse mecanismo para incorporar novas informações nas classes, permitindo o desenvolvimento de novas funcionalidades. Essas adições nas anotações visam uma maior expressividade das informações e simplificação na sintaxe, além de introduzi-las em novos locais abrindo uma nova gama de possibilidades para sua utilização. "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB Suporte de linguagem à modularização As JSRs 277 (focando em deployment e empacotamento) e 294 (focando em modularidade de APIs em tempo de desenvolvimento) introduzem o conceito de superpackages. O problema que este conceito procura solucionar diz respeito a relações hierárquicas entre pacotes. Por exemplo, ao tornar uma classe pública, ela passa a fazer parte de uma determinada API. Listagem 1. Exemplo de superpackage. superpackage com.mundoj.java7 { // Pacotes membros member package com.mundoj.java7; member package com.mundoj.java7.interview; member package com.mundoj.java7.typeannotations; Atualmente, não existe uma forma nativa de dizer que uma classe, mesmo pública, é pública somente para alguns pacotes e não acessível (algo como o modificador de acesso default) para outros pacotes. Existem ferramentas, como JDepend, por exemplo, que podem prover esse tipo de verificação antes ou depois da compilação, mostrando que existe no mercado essa necessidade. Os superpackages fornecem uma forma de expressar essa relação hierárquica entre pacotes, que podem ter membros públicos para a implementação de determinados pacotes, porém que não devem ser visíveis para outros pacotes. Assim, pacotes que estão organizados dentro de um superpackage podem acessar classes públicas entre si, mas ao mesmo tempo, pacotes que estão fora deste superpackage não podem acessar essas classes, a menos que estas sejam exportadas. O exemplo da listagem 1 mostra como os superpackages devem aparecer na linguagem: Na verdade, um superpackage é uma unidade de compilação onde serão definidos os pacotes que terão acesso a classes públicas de outros pacotes e quais classes públicas poderão ser vistas de fora dessa unidade de compilação. Para se definir um superpackage, será preciso criar um arquivo super-package. java no local onde este será definido. Para o exemplo da Listagem 1, o caminho completo do arquivo é com/mundoj/java7/super-package.java. Nele, é definido que os pacotes com.mundoj.java7, com.mundoj.java7.interview e com.mundoj.java7.typeannotations farão parte deste superpackage, assim como os superpackages com.mundoj.java5 e com.mundoj.java6. A classe HelloWorld é exportada, o que significa que esta classe estará disponível fora deste superpackage. O pacote membro com.mundoj.java7.interview também é exportado, o que significa que seus membros públicos também poderão ser acessados de fora deste superpackage, ao contrário do pacote com.mundoj. java7.typeannotations, que não é exportado. Mesmo sendo uma característica nova na linguagem, não será obrigatória sua utilização, podendo assim ser ignorada. Isto é essencial para que haja compatibilidade com código já desenvolvido, o que é uma das principais preocupações ao se introduzir uma nova funcionalidade na linguagem. Essa é uma característica que possibilita a relação entre pacotes em nível de classe, mas ainda não existe nada que possa informar que um método deve ser público para determinados métodos e não acessível para outros métodos. Pequenas melhorias na linguagem – Project Coin O objetivo do Project Coin é determinar o conjunto de pequenas alterações de linguagem que deve ser adicionado ao Java 7. A submissão de propostas foi até o dia 30 de março deste ano e os interessados deveriam preencher um formulário com diversos itens que descrevem a mudança desejada. Essa proposta deve descrever os seus benefícios e as desvantagens da proposta, além de apresentar alternativas existentes sem mudanças na linguagem. Também deveria ser mostrado um exemplo simples, com o objetivo de exemplificar rapidamente o uso da nova funcionalidade, e um exemplo avançado, que deve mostrar como seria o // Superpackages membros member superpackage com.mundoj.java5; member superpackage com.mundoj.java6; // Classe exportada (pode ser acessada de fora do superpackage) export com.mundoj.java7.HelloWorld; // Pacote exportado export package com.mundoj.java7.interview; } funcionamento em casos mais complexos. Outra parte importante da proposta é a que analisa o impacto que elas teriam no que já existe hoje, como na gramática da linguagem e no formato das classes compiladas em bytecode. Também se deve levar em consideração como essa funcionalidade pode ser testada, se é necessário o suporte de bibliotecas e se são necessárias alterações na API de reflexão. Mudanças em outras ferramentas e partes da plataforma como serialização e o javadoc também devem ser consideradas. Na parte final da proposta, um quesito que é crítico na aceitação da mudança diz respeito à compatibilidade: essa mudança quebra algum código existente? Como programas feitos na versão anterior interagem com esta funcionalidade? As seções seguintes descrevem algumas das propostas do Project Coin mais comentadas e com mais probabilidade de serem incorporadas na linguagem Java. É importante ressaltar que essas ainda são propostas e que, ao serem consideradas dentro de uma JSR, podem sofrer modificações, serem deixadas para versões futuras ou mesmo serem definitivamente descartadas. B &TUSVUVSBTTXJUDIDPNTVQPSUFQBSB4USJOHT Atualmente, estruturas switch do Java suportam somente os tipos primitivos byte, short, char, int, as classes wrapper (Character, Byte, Short, Integer) e constantes enum. Alguns desenvolvedores utilizam o hashCode (que na verdade é um inteiro) de variáveis String para sua utilização no case do switch, simulando de certa forma a utilização de Strings em estruturas switch. Um dos problemas dessa abordagem é que o código fica pouco legível. Muito possivelmente, a versão 7 da linguagem Java suportará também Strings em estruturas switch de forma nativa. Desta forma, será possível escrever o código apresentado na Listagem 2. A estrutura de controle switch não é uma das favoritas na comunidade de desenvolvimento, sendo que seu uso é classificado por alguns como uma má prática. O próprio livro de Martin Fowler sobre refatoração fala que em cada switch existe um "mal cheiro", para o qual vale a pena verificar se não deve ser utilizado polimorfismo. Dessa forma, pode-se questionar se realmente vale a pena investir nessa modificação, que talvez incentive ainda mais o uso dessa estrutura. 43 "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB Listagem 2. String em uma estrutura switch. Listagem 5. Chamadas encadeadas. public void ouvirMusica(String nome) { switch (nome) { case “Beto”: { System.out.println(“Vamos ouvir John Lennon ou Pink Floyd?”); break; } case “Guerra”: { System.out.println(“Vamos ouvir Jewel ou Alanis Morissette?”); break; } default: { System.out.println(“Vamos ouvir Led Zeppelin!”); } } public class Pessoa { C 0QFSBEPSFTEFDPNQBSBÎÍPFNFOVNT Enums são ordenados por natureza. Atualmente, para se comparar constantes enum, utiliza-se comumente o método compareTo(), que é disponível implicitamente a tipos enum. Embora funcione, é pouco intuitivo e pouco legível. Por exemplo, atualmente, podemos comparar constantes enum conforme apresentado na Listagem 3. private String nome; private int idade; // Métodos getter e setter omitidos } public void cumprimentar() { System.out.println(“Ola, meu nome eh “ + nome + “!”); } // Instanciando um objeto da classe Pessoa Pessoa pessoa = new Pessoa(); // Chamadas encadeadas pessoa.setNome(“Beto”).setIdade(26).cumprimentar(); já existentes, causando sintaxes e usos inesperados da mesma. Isso pode gerar uma fonte de novos bugs e até mesmo quebrar código já existente, o que é algo indesejado em qualquer nova funcionalidade da linguagem. Como essas funcionalidades ainda são apenas propostas, é preciso esperar para ver se ela será mesmo implementada ou se haverão mudanças na ideia original. Listagem 3. Comparação de constantes enum com o método compareTo(). E *OGFSÐODJBEFUJQPTFNDMBTTFTHFOÏSJDBT enum Tamanho {PEQUENO, MEDIO, GRANDE} Uma das ideias que poderão surgir como melhoria na linguagem é a inferência de tipos ao instanciar classes genéricas. Por exemplo, atualmente, para se instanciar um HashMap (que é uma classe genérica), é preciso declarar a variável com as mesmas informações do tipo. Um artifício comumente utilizado por desenvolvedores é a utilização de fábricas estáticas para que isso não seja preciso. A Listagem 6 ilustra as duas abordagens. Tamanho meuTamanho = Tamanho.GRANDE; Tamanho seuTamanho = Tamanho.MEDIO; if (meuTamanho.compareTo(seuTamanho) > 0) { System.out.println(“Minha camisa vai ficar um pouco grande em voce!”); } Na Listagem 3, o resultado do método compareTo() é 1 porque a constante GRANDE aparece uma posição depois da constante MEDIO. Assim, se existisse outra constante chamada MUITO_GRANDE depois da constante GRANDE, e seu valor fosse atribuído à variável meuTamanho, então o resultado da chamada do método compareTo() seria 2. Além da forma apresentada na Listagem 3, os operadores de comparação == e != também podem ser utilizados. A partir do Java 7, muito possivelmente, também poderão ser utilizados os outros operadores de comparação, como exemplificado na Listagem 4. Espera-se também que o desempenho dessa comparação seja melhorado com essa modificação. Listagem 4. Comparação de constantes enum no Java 7. enum Tamanho {PEQUENO, MEDIO, GRANDE} Tamanho meuTamanho = Tamanho.GRANDE; Tamanho seuTamanho = Tamanho.MEDIO; if (meuTamanho > seuTamanho) { System.out.println(“Minha camisa vai ficar um pouco grande em voce!”); } D .ÏUPEPTWPJEJNQMJDJUBNFOUFSFUPSOBOEPUIJT Outra nova funcionalidade interessante que possivelmente aparecerá na próxima versão da linguagem são as chamadas encadeadas, em que métodos void implicitamente retornarão this (que se refere sempre ao objeto atual). Isto tornará a “fluent interface” mais fácil de ser utilizada. A Listagem 5 mostra um exemplo de chamadas encadeadas. Uma das críticas a essa funcionalidade é que ela irá alterar o uso de APIs 44 www.mundoj.com.br Listagem 6. Instanciando classes genéricas. // Criando um HashMap a partir do Java 5 Map<Integer, String> mapa = new HashMap<Integer, String>(); // Criando um HashMap utilizando uma fábrica estática Map<Integer, String> outroMapa = makeHashMap(); // Fábrica estática de HashMap static <K, V> HashMap<K, V> makeHashMap() { return new HashMap<K,V>(); } A partir do Java 7, a classe genérica possivelmente poderá assumir automaticamente a mesma parametrização do tipo da variável. Assim, a expressão de criação de instâncias de classes genéricas será da forma mostrada na Listagem 7. Listagem 7. Instanciando classes genéricas a partir do Java 7. Map<Integer, String> mapa = new HashMap<>(); List<String> lista = new ArrayList<>(); Além disso, a partir do Java 7, as informações sobre os generics poderão estar disponíveis também em tempo de execução. Quando os generics foram introduzidos na linguagem, foi definido que suas informações estariam disponíveis somente em tempo de compilação, para que houvesse compatibilidade com códigos criados antes dos generics. Na próxima versão do Java, existe a possibilidade que essas informações sobre o tipo genérico de uma instância também possa ser acessada em tempo de execução através da API Reflection. "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB F $MÈVTVMBDBUDIBQSJNPSBEB Possivelmente, a cláusula catch de tratamento de exceções terá duas mudanças significativas. A primeira delas é que será possível “pegar” mais de uma exceção por bloco e tratá-las igualmente. Muitas vezes, o tratamento que precisa ser dado a exceções em um bloco que pode lançar mais de um tipo de exceção é igual para todas. Isso é especialmente problemático quando essas exceções são de hierarquias distintas. Por exemplo, atualmente, é comum encontrarmos um código como o da Listagem 8. Listagem 8. Mesmo tratamento aplicado a mais de uma exceção. try { return klass.newInstance(); } catch (InstantiationException exception) { throw new AssertionError(exception); } catch (IllegalAccessException exception) { throw new AssertionError(exception); } A partir do Java 7, deverá ser possível “pegar” várias exceções na mesma cláusula catch e tratá-las da mesma forma. A Listagem 9 mostra como isso poderá ser feito. Listagem 9. Tratamento de exceções no Java 7. try { return klass.newInstance(); } catch (InstantiationException | IllegalAccessException exception) { throw new AssertionError(exception); } Além disso, a cláusula catch também deve sofrer outra alteração significativa. Em algumas situações, muitos desenvolvedores escolhem definir somente uma cláusula catch para tratar quaisquer exceções que possam ser lançadas a partir de um bloco try, e após tratá-las, relançam estas exceções. Para isso, basta definir uma única cláusula catch com um tipo mais amplo do que as exceções que podem ser lançadas no bloco try (como Throwable, por exemplo). O problema é que neste caso, o tipo da exceção pode se tornar mais amplo do que as exceções indicadas na assinatura do método, o que torna impossível relançar as exceções no momento do tratamento. Por exemplo, o código apresentado na Listagem 9 causará um erro de compilação. Listagem 9. Erro de compilação ao lançar uma exceção mais ampla do que as indicadas na assinatura do método. public void metodoArriscado() throws IOException, RemoteException { try { // Bloco try lançando vários tipos de exceções } catch (Throwable exception) { // A exceção poderia ser tratada aqui throw exception; // Erro de compilação: Unhandled exception type Throwable } } O erro de compilação mostrado na Listagem 9 acontece porque o tipo Throwable é mais amplo do que IOException ou RemoteException, que são as exceções que o método pode lançar. A partir do Java 7, será possível aplicar o mesmo tratamento para quaisquer exceções e relançá-las, mesmo que o tipo indicado na cláusula catch seja mais amplo do que os indicados na assinatura do método. Por exemplo, o código da Listagem 10 mostra como esse tipo de comportamento será implementado no Java 7. Listagem 10. Código válido a partir do Java 7. public void metodoArriscado() throws IOException, RemoteException { try { // Bloco try lançando vários tipos de exceções } catch (final Throwable exception) { // A exceção poderia ser tratada aqui throw exception; // Sem erro de compilação } } Ao adicionar a palavra-chave final à declaração da exceção na cláusula catch, o compilador entende que as únicas exceções verificadas que podem estar sendo tratadas são as lançadas pelo bloco try, e desde que essas exceções estejam declaradas na assinatura do método, é possível relançá-las, mesmo que o tipo indicado na cláusula catch seja mais amplo do que os indicados na assinatura do método. G &YUFOTJPO.FUIPET Uma vez que uma interface está definida, não é possível adicionar novos métodos sem quebrar as implementações existentes. Isso porque as classes que implementam a interface não possuem implementações dos novos métodos. Por exemplo, ao adicionar novos métodos à interface java.sql. ResultSet, todas as implementações atuais se tornariam inválidas. Como forma de contornar este problema, em algumas APIs existem métodos estáticos utilitários que criam novas funcionalidades, como exemplo, pode ser citada a classe Collections. Esses métodos são pouco convenientes de serem utilizados e dificultam a legibilidade do código. Uma proposta de funcionalidade feita por Neal Gafter, chamada de Extension Methods, seria a possibilidade de adicionar métodos estáticos a interfaces já existentes. A versão mais simples dessa proposta, exemplificada na Listagem 11, permite que métodos importados estaticamente possam ser utilizados como membros do seu primeiro argumento. Listagem 11. Exemplo de métodos de extensão. // Importação estática de métodos import static java.util.Collections.sort; ... List<String> list = new ArrayList<>(); // É como se o método sort() fizesse parte da interface List, mas na verdade // em tempo de compilação isso é convertido para a chamada sort(list) list.sort(); Essa proposta gerou discussões como possíveis conflitos de nome gerados por importações estáticas e a possibilidade de adicionar métodos em classes com o modificador 'final', o que poderia gerar uma nova fonte de bugs em aplicações. Como forma de contornar esses problemas, foi proposto que os métodos nos quais isso poderia ser utilizado deveriam ser marcados através de uma nova sintaxe ou através de uma anotação. Outra modificação na proposta original seria a dos métodos invocados dessa forma possuírem uma sintaxe diferenciada, como list.do.sort() ou list->sort(). Essas propostas entram em conflito com um dos objetivos de Neal Gafter quando fez a proposta, que era de simplificar esse tipo de chamada sem modificar a sintaxe da linguagem. Devido a essas questões, talvez essa funcionalidade acabe sendo deixada de fora, porém isso só o tempo irá dizer. 45 "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB 0RVFQPEFWJSEFQPJTEP+BWB Apesar das mudanças do Java 7 parecerem significativas, ainda existem diversas propostas de mudanças que não entrarão nessa versão e que muitos apostam que no futuro podem ser incorporadas na linguagem. As duas subseções seguintes irão falar um pouco sobre duas funcionalidades sobre as quais existe bastante discussão de quando e como elas serão inseridas na linguagem. A primeira delas são as closures, cuja não-inclusão no Java 7 foi a grande decepção da comunidade. A segunda funcionalidade é a programação orientada a aspectos, que é um paradigma que vem ganhando cada vez mais adeptos. Closures Embora mencionado na entrevista, os closures muito provavelmente aparecerão na linguagem somente em alguma versão posterior ao Java 7. Esta funcionalidade (ou pelo menos algo parecido) já é suportada em várias linguagens atualmente, a maioria delas dinâmicas. Quase todas as linguagens que foram introduzidas há menos de dez anos suportam algo do tipo. Ainda não existe um consenso sobre como eles aparecerão na linguagem Java; atualmente existem várias propostas, sendo três delas principais. Existem várias definições para closure, sendo que a mais importante é função anônima. A teoria da Ciência da Computação chamaria isso de expressão lambda, e na verdade este conceito não é novo; datando de trabalhos teóricos da década de 1930. As implementações mais antigas e importantes foram nas linguagens Scheme e Smalltalk. Um fato curioso é que praticamente todas as linguagens que podem ser executadas na JVM, exceto a linguagem Java, possuem algo parecido com closures. Embora ainda não exista sequer uma JSR para os closures, é possível ter uma ideia de como essa funcionalidade poderia ser inserida na linguagem. A Listagem 12 dá o exemplo baseado em uma das propostas, de como os closures poderiam ser implementadas. Nesse exemplo, closure é definida como um tipo que recebe parâmetros e retorna um resultado. É como um trecho de código que pode ser passado como parâmetro e possui acesso a elementos locais relativos a onde a closure foi definida. Assim, closure é uma função anônima, que pode ou não definir parâmetros e até variáveis internas, e pode aparecer na forma de atribuição ou ser somente uma expressão de resultado. Este tipo de funcionalidade seria de grande utilidade, por exemplo, na API de concorrência. Uma Thread poderia ser criada a partir de uma closure com o código que ela deveria executar. O uso de closures tornaria o código menos verboso e mais simples de ser entendido. Não está no escopo deste artigo descrever com detalhes uma ou mais propostas de implementação de closures na linguagem Java (mesmo porque isso por si só já daria um grande artigo). Nosso objetivo é apresentar de uma forma breve o que são as closures e tentar especular um pouco a respeito de sua implementação nativa na linguagem. Houve uma grande decepção na comunidade a respeito das closures não terem sido incluídas dentre as funcionalidades do Java 7, pois foram feitos diversos protótipos e foi criada uma grande expectativa a respeito. Na conversa da equipe da revista com Alex, ele deu a entender que as closures muito provavelmente estarão no futuro da linguagem. Dentre os fatores que pesaram para deixá-las fora do Java 7 está a questão de que quando uma funcionalidade é adicionada na linguagem, não é mais possível adotar uma abordagem diferente e cria-se um compromisso de 46 www.mundoj.com.br Listagem 12. Exemplos de Closures. // Um closure que soma dois números {int, int => int} soma = {int x, int y => x + y}; int total = soma.invoke(2, 2); // Um closure que não retorna valor; somente efetua uma operação {String => void} cumprimentar = {String nome => System.out.println(“Ola, “ + nome);}; cumprimentar.invoke(“Beto”); compatibilidade em todas as versões futuras. Com a diversidade das propostas, talvez tenha se percebido que se deve estudar mais a fundo qual seria a forma mais adequada de introdução de closures na linguagem. Aspectos nativos na Linguagem Java? A programação orientada a aspectos é um paradigma de programação (porém que alguns consideram apenas uma nova técnica de modularização) que tem como objetivo a separação dos interesses transversais em uma aplicação. Os interesses transversais caracterizam requisitos funcionais ou não-funcionais que normalmente estão espalhados pelo código, pois estão presentes em diversos pontos da aplicação. Exemplos comuns desse tipo de interesse são segurança, transacionalidade e logging, porém existem diversos exemplos de requisitos funcionais que também possuem essa característica. Como está fora do escopo deste artigo descrever esse tipo de paradigma, sugere-se aos leitores que quiserem se aprofundar no tema a leitura de material a respeito. No Brasil e na América Latina, um dos principais fóruns acadêmicos na área de aspectos é o LA-WASP (Latin-American Workshop on Aspect-oriented Software Development). No ano de 2008, esse evento contou com o painel “Adoption of AOSD in Industry”, em que renomados profissionais e pesquisadores da área discutiram sobre a adoção dos aspectos pela indústria. O painel (figura 2 – participantes citados da esquerda para direita) contou com o professor Paulo Borba, UFPE, como moderador e com a participação de Flávia Raimone, da JBoss Brasil e desenvolvedora do JBoss AOP, com o professor Kevin Sullivan, da University of Virginia, e com Paulo Merson, do Software Engineering Institute (SEI), Universidade Carnegie Mellon. Houve certo consenso de que a adoção desse paradigma de programação pela indústria ainda é modesto, porém muitos projetos utilizam softwares que possuem o uso de aspectos encapsulados dentro de sua implementação. Um grande exemplo é o próprio servidor de aplicações JBoss. Este painel foi citado, pois uma das perguntas feitas aos participantes foi a respeito do que é necessário para que esse paradigma seja mais adotado pela indústria. Uma das respostas foi o suporte nativo à programação orientada a aspectos pela linguagem Java. Infelizmente, pela conversa com Alex Buckley, é possível perceber que isso é algo que ainda está longe de acontecer. Ao ser perguntado sobre esta possibilidade, a resposta de Alex foi que, embora os aspectos já sejam bastante maduros, ao colocá-los na linguagem, isto significaria que todos os desenvolvedores Java precisariam aprender sobre o assunto, desde desenvolvedores de dispositivos móveis até programadores de supercomputadores. Ele citou Bjarne Stroustrup (o desenvolvedor da linguagem C++), que há algum tempo falou sobre as propostas de melhorias que ele recebe e leva para o C++ International Working Group. Ele disse publicamente que, se uma proposta não é útil para pelo menos 200 mil desenvolvedores, esta proposta não será nem considerada. No contexto da linguagem Java, este número pode ser projetado para mais ou menos dois milhões de desenvolvedores. Por esta razão, tão raramente uma proposta de melhoria é aceita. "SUJHPt"&WPMVÎÍPEB-JOHVBHFN+BWB Outra questão que deve ser considerada é que hoje Java é ensinada em diversas universidades do mundo como primeira linguagem de programação. A adição desses novos conceitos nativamente na linguagem aumentaria sua complexidade e dificultaria seu aprendizado. Se para o paradigma orientado a objetos foram necessários diversos anos de sua adoção na indústria para o amadurecimento de certos conceitos, imagine para a orientação a aspectos que ainda colhe seus primeiros frutos de sua utilização com sucesso no mercado. Na academia, existem várias pesquisas sobre o assunto, que procuram, por exemplo, definir o significado dos chamados adendos (advices) e como expressar melhor os pontos de junção (joint points) de um aspecto. A incorporação dos aspectos de forma nativa, provavelmente iria frear pesquisas sobre novas formas de expressar aspectos, prendendo a linguagem Java àquela implementação em todas as versões subsequentes por questões de compatibilidade. De certa forma, ao adicionar uma nova funcionalidade, a linguagem está concordando em suportá-la para sempre. Existem diversas implementações de aspectos na linguagem Java, podendo ser citados como exemplos o AspectJ, o Spring AOP e o JBoss AOP. O AspectJ é uma das implementações mais populares e mais completas, que define uma nova sintaxe para aspectos, porém em sua última versão, também suporta o uso de anotações para algumas coisas. O Spring AOP utiliza a própria linguagem Java para as definições realizando configurações adicionais em seus arquivos XML ou utilizando anotações. Ainda existem diversas opções para os aspectos serem agregados às classes (processo conhecido como weaving), como em tempo de compilação, em tempo de carregamento e em tempo de execução. Que tipo de abordagem é mais adequada para a linguagem Java? Apesar da tecnologia de aspectos já ter amadurecido bastante nos últimos anos, será que a comunidade está preparada para escolher uma delas para ser incorporada à linguagem, abandonando as outras abordagens e tendo que suportar para sempre a que foi escolhida? Esse foi o tipo de indagação que Alex Buckley apresentou sobre o assunto em sua conversa com a revista. A implementação de aspectos dentro de uma linguagem como Java não é apenas uma adição pontual na linguagem como as outras funcionalidades apresentadas neste artigo, e sim uma mudança profunda que afetaria o sistema de tipos e a própria arquitetura da máquina virtual. Segundo Alex, adicionar aspectos, pontos de junção e adendos como tipos da linguagem é algo complicado, principalmente quando é preciso manter a compatibilidade com o código legado. Porém os fãs de aspectos não devem ficar decepcionados com a notícia, pois a máquina virtual Java está dando cada vez mais um suporte melhor a outras linguagens. Como foi citado, no Java 7 está se trabalhando para que a invocação de métodos de outras linguagens não-Java tenham um desempenho similar ao das invocações nativas. O próprio AspectJ pode ser considerado uma linguagem que compila para bytecode e executa dentro da máquina virtual. Dessa forma, o fato de Java ainda não ter planos de suportar nativamente aspectos, não significa que não podemos utilizar aspectos com a linguagem Java, e sim que talvez ainda não seja a hora de uma implementação ser fixada e de todos os desenvolvedores precisarem aprender esses novos conceitos. Considerações finais As alterações realizadas na linguagem no Java 5 trouxeram diversas lições. Uma delas foi que evoluções na linguagem podem trazer diversos benefícios, como os tipos genéricos, que ajudaram na criação de um código mais seguro, e as anotações, que criaram diversas novas possibilidades através da 'JHVSB-"8"41o1BJOFMoi"EPQUJPOPG"04%JO*OEVTUSZw adição de novas informações às classes. Outra lição foi que é preciso ter muito cuidado com essas alterações, para que elas sejam compatíveis com código já existente. Como foi dito na entrevista, a adição da palavra-chave 'enum' quebrou muitos códigos que utilizavam essa palavra como um identificador. Este artigo apresentou com exclusividade uma entrevista com Alex Buckley, que falou sobre sua experiência na evolução da linguagem Java e a respeito do que podemos esperar para o seu futuro. Também foram vistas as principais funcionalidades de linguagem que devem entrar no Java 7, assim como certa previsão sobre quando funcionalidades como closures e aspectos devem aparecer nativamente na linguagem. Apesar de Alex Buckley ter dito que uma linguagem é melhor quando ela possui menos funcionalidades, é possível perceber pelas respostas e pelas propostas de funcionalidades para o Java 7 que a linguagem não está no caminho da simplicidade. Em uma entrevista com James Gosling realizada por Paulo Silveira e Guilherme Silveira em um JavaOne foi possível perceber essa mesma tendência. As novas funcionalidades certamente são interessantes e permitem, por exemplo, a detecção prematura de defeitos e uma melhor modularização. Isso revela uma tendência em evoluir a linguagem na busca de uma maior segurança de código mesmo que isso torne a codificação mais complexa, o que acaba indo na direção oposta de outras linguagens que procuram cada vez mais a simplificação na busca de uma maior produtividade. Vale a pena lembrar que, neste momento, as funcionalidades apresentadas ainda são apenas propostas, sendo que algumas delas não possuem nem JSR ainda. Dessa forma, se você quer participar dessa evolução e dar sua opinião sobre como deve ser a implementação em cada uma delas, é só se envolver e dar sua contribuição. O futuro do Java, na verdade, está em nossas mãos! Referências t (SÈmDPEFQSPKFUPTQPSMJOHVBHFNEFQSPHSBNBÎÍPOP4PVSDF'PSHFoIUUQXXXDTCFSLFMFZ edu/~flab/languages.html t 8IFO :PV 4IPVME +VNQ +34 5IBUhT 8IFO IUUQXXXNJDIBFMOZHBSEDPN CMPHXIFO@TIPVME@ZPV@KVNQ@KTS@@UIUNM t 8IBU)BUI+BWB8SPVHIUoIUUQCDTRVBSFECMPHTQPUDPNXIBUIBUIKBWBXSPVght.html t +43"OJNPTJUZoIUUQXXXDPODVSSFOUBõBJSPSHKTSBOJNPTJUZ t 5IF $IFDLFS 'SBNFXPSL .BOVBM o IUUQHSPVQTDTBJMNJUFEVQBHKTSDVSSFOUDIFDLFST manual.pdf t 5ZQF"OOPUBUJPOT4QFDJmDBUJPO+43 oIUUQHSPVQTDTBJMNJUFEVQBHKBWBSJKBWBBOOPtation-design.pdf t i5IFSFhTOPUBNPNFOUUPMPTFwo.BSL3FJOIPMET#MPHoIUUQCMPHTTVODPNNSFOUSZKJHTBX t 0QFO+%,o1SPKFDU$PJOoIUUQPQFOKELKBWBOFUQSPKFDUTDPJO t 0QFO+%,o+%,'FBUVSFTIUUQPQFOKELKBWBOFUQSPKFDUTKELGFBUVSFT t $MPTVSFTGPSUIF+BWB1SPHSBNNJOH-BOHVBHFIUUQKBWBDJOGP t $MPTVSFT 1SPUPUZQF 6QEBUF BOE &YUFOTJPO .FUIPET o IUUQHBGUFSCMPHTQPUDPN DMPTVSFTQSPUPUZQFVQEBUFBOEFYUFOTJPOIUNM t &YUFOTJPO.FUIPET1SPQPTBMToIUUQXXXPSFJMMZOFUDPNPOKBWBCMPHFYUFOTJPO@ NFUIPET@QSPQPTBMTIUNM t /FBM(BGUFSTCMPHIUUQHBGUFSCMPHTQPUDPN t +BWB1SFEJDUJPOTIUUQKBWBE[POFDPNOFXTKBWBQSFEJDUJPOT t +BWB4&1SPKFDU+JHTBX.PEVMBSJ[JOH+%,5IF1MBOFUBSJVNIUUQCMPHTTVODPNUIFQMBOFUBSJVNFOUSZQSPKFDU@KJHTBX@NPEVMBSJ[JOH@KEL@ 47