Não posso descobrir nem instanciar tipos genéricos! Porque?

Por Paulo Silveira em 28/04/08

São incontáveis os posts no GUJ com uma pergunta semelhante: “Posso extrair o nome de um tipo genérico?“, “Não consigo extrair tipo do genérico!“, “Utilizando generics para instanciar objetos“, entre outros. Curioso que esse tipo de pergunta tem aumentado muito nos últimos tempos, identificando um possível crescimento no uso do Java 5 em diante. Já era a hora!

Eu já havia postado sobre isso quando falei de reificação de tipos parametrizados, mas de uma maneira mais geral.

A questão básica é a seguinte: Se eu tenho um tipo genérico, que recebe um tipo parametrizado T como argumento, eu posso instanciar T de alguma forma?

Quem sabe tentar assim:

class Dao<T> {
  public T cria() {
    return new T();
  }  
}

Essa forma não funciona. Você não tem garantias sobre os construtores que o tipo T possui. E então se tentarmos assim:

class Dao<T> {
  public T cria() {
    return T.class.newInstance();
  }  
}

Aqui a sintaxe até poderia ser possível, mas infelizmente o java não sabe quem é T nem mesmo em tempo de execução. Nem mesmo com manipulação de bytecode ou qualquer outro recurso. Isso porque o compilador “apaga” essa informação depois de utilizada: é a tal da mal falada erasure.

Qual seria a vantagem da erasure?

Antes de mais nada: erasure não serve para poder rodar código do Java 5 em VMs Java 1.4 ou menor! Não é esse o objetivo.

Quando a JSR14 do generics foi proposta, eles queriam mais que compatibilidade para trás em relação a compilação, eles queriam também a possibilidade de migrar o código antigo para poder usar código novo: uma ArrayList precisa poder ser passada como argumento para alguém que receba List<QualquerClasse> como argumento!

Para ilustrar a situação, imagine que eu tenho uma aplicação grande X que usasse a classe ArrayList em muitos lugares (como a grande maioria das aplicações). Essa aplicação X usa a biblioteca B, que recebe List como argumento em muitos de seus métodos. Se um dia a bibioteca B passasse a usar List genérica, gostaríamos que a aplicação X atualizasse B sem maiores problemas: sem precisar recompilar nada, nem trocar nada no código fonte. O Neal Gafter escreveu muito sobre isso pouco antes da release do Java 5, dada as inúmeras críticas que eles estavam recebendo.

Se o Java tivesse optado por outra maneira de implementar generics, teria ou quebrado compatibilidade com o uso não genérico de classes que viraram genéricas, ou precisaria criar classes paralelas as atuais, praticamente copiadas e coladas, só que estas genéricas, tornando as antigas deprecated ou legadas.

Como o .NET resolveu o mesmo problema?

O .NET seguiu essa segunda forma, e criou uma hierarquia quase que paralela de coleções dentro do name space System.Collections.Generic.

A IList é a interface que define as operações em uma lista não genérica, e a ArrayList é sua implementação mais comumente usada. Quando entrou generics no .NET eles criaram uma outra interface para a lista, com mesmo nome, só que genérica: a IList<T>.

A classe List já é a implementação da interface genérica, e é ela quem você vai usar em vez de ArrayList. Ela possui uma definição bem estranha:

public class List : IList, ICollection,
    IEnumerable, IList, ICollection, IEnumerable

Ela implementa tanto a lista genérica como a não genérica. O .NET tem um recurso que nós não temos, que faz com que apesar dessa lista também implementar a interface não genérica, você só consegue invocar os métodos não genéricos (que trabalham com Object) se estiver se referenciando a ela explicitamente como uma lista não genérica, como o código abaixo:

IList x = new List<String>();
x.Add(2);

Apesar desse código compilar, no .NET temos essa informação dos tipos genéricos em tempo de execução, o que fará gerar uma exceção:

System.ArgumentException: The value "2" is not of type "System.String" e cannot be used in this generic collection

No Java teríamos apenas um unchecked warning na linha da declaração da referência, e uma possível ClassCastException mais a frente no código.

Aqui a vantagem é você poder passar uma List genérica para um código .NET antigo, que recebe como argumento uma IList não genérica. Além disso, no .NET você pode sim descobrir quem é T em tempo de execução:

class ClasseGenerica<T> {
  void metodo() {  Console.WriteLine(typeof(T))}
}

Em resumo: o sistema de generics do .NET é realmente seguro, não temos como burlá-lo através de unchecked casts, como ocorre em Java. O Java novamente sacrificou alguns recursos interessantes em favor a compatibilidade de versões e interoperabilidade entre classes genéricas e as não genéricas já existentes. Cada um com sua vantagem. Como citei no outro artigo, existem algumas idéias de dar suporte a tipos genéricos reificados no Java, ao mesmo tempo que outros ficariam ainda com a erasure, sendo que você pode escolher qual o que te agrada para aquela classe genérica em particular. Talvez ter as duas opções adicione ainda mais complexidade a tipagem genérica do Java, mas eu particularmente gosto da idéia.

Mais uma vez um post que era para ser sucinto ficou longo. Agradeço ao Rafael Steil e Rodrigo Kumpera pela colaboração, e ao Lucas Cavalcanti e Guilherme Moreira pedindo para que fosse elaborado um post mais completo sobre esse assunto tão pertinente.

Entendendo o serialVersionUID

Por Paulo Silveira em 01/04/08

O serialVersionUID é uma dúvida constante entre muitos desenvolvedores. Afinal, quando e para que exatamente usá-lo? Devo gerar um número aleatório bem grande, ou um número qualquer? Essas perguntas são comuns, e ao desenvolvedor experiente é necessário conhecer a fundo esse detalhe do processo de serialização do Java.

Quando um objeto é serializado no Java, essa sequência de bytes, além de conter seus atributos de instância não transientes, carrega consigo um número que indentifica a “versão” da classe que foi usada durante o processo. Esse é o chamado serialVersionUID, ou seja, o indentificador de versão de serialização de uma classe. Esse número é utilizado para saber se o objeto que estamos recuperando é de uma versão “compatível” com a versão da classe que foi utilizada quando serializamos o objeto: em outras palavras, os arquivos .class não precisam ser necessariamente os mesmos para que o processo de serialização ocorra com sucesso.

Por exemplo, considere a seguinte classe Usuario:

package br.com.caelum;

public class Usuario implements Serializable {
  private String login;
}

Essa classe possui o serialVersionUID igual a 2806421523585360625L. Esse número não é aleatório! Ele é um hash (SHA) calculado em cima dos nomes dos seus atributos, e assinaturas dos métodos em uma ordem bem definida pela especificação do processo de serialização. E como eu sei esse número? O JDK vem com a ferramenta serialver, que implementa esse mesmo hash:

serialver br.com.caelum.Usuario

Se o serialVersionUID utilizado durante a serialização não bater exatamente com o serialVersionUID da classe que está sendo usada para recuperar essa informação, uma exception é lançada: java.io.InvalidClassException.

Por exemplo, se adicionarmos um novo atributo na nossa classe Usuario:

public class Usuario implements Serializable {
  private String login;
  private String senha;
}

Agora teremos o serialVersionUID valendo 416295346730660862L. Caso você serialize um Usuario com a primeira classe aqui definida, e tentar recuperar essa informação usando essa nova versão de classe, receberemos a conhecida java.io.InvalidClassException. Esse é o comportamente que em muitos casos queremos, mas algumas vezes fazemos pequenas modificações na classe as quais percebemos que não impactarão no processo de serialização, e precisamos manter compatibilidade com a versão antiga daquela classe. Para isso, basta definirmos explicitamente qual é o nosso serialVersionUID, e no caso de querer manter compatibilidade com a classe Usuario anterior, vamos utilizar o valor de serialVersionUID que seria gerado pela JVM: 2806421523585360625L. O código ficaria:

public class Usuario implements Serializable {
  private static final long serialVersionUID = 2806421523585360625L;
  private String login;
  private String senha;
}

Às vezes recebemos um warning do Eclipse, e ele pede para que seja definido o serialVersionUID da classe em questão. Isso ocorre porque você implementa Serializable ou uma de suas mães a implementa. O Eclipse então te abre três opções: utilizar o @SurpressWarnings para você assumir o risco, usar um valor default, ou usar o valor gerado. O gerador de UIDs do Eclipse é exatamente o mesmo gerador utilizado pelo Java SE para criar os UIDs padrão! Reforçando, esse número não é um número aleatório!

serialVersionUID

Quando alguém esquece de manter o mesmo serialVersionUID para duas versões compatíveis de uma classe, podemos ter problemas em usar diferentes versões do software que são teoricamente compatíveis. Isso muitas vezes acontece em servidores de aplicação, e se seu cliente esta desatualizado em relação a versão dos jars necessários pelo servidor, podemos ter alguns InvalidClassExceptions que poderiam ser facilmente evitados se o serialVersionUID tivesse sido corretamente aplicado. Claro que algumas outras vezes as versões realmente não são compatíveis e a exception procede.

Esse grave problema pode acontecer mesmo usando classes do Java SE entre diferentes versões, como é o caso da classe java.text.AttributedCharacterIterator.Attribute (utilizada pela java.awt.Font). Do Java 1.3 para o Java 1.4 essa classe foi levemente alterada, e o serialVersionUID gerado pelo algoritmo da JVM desta classe mudou de -1514471214376796190L para -9142742483513960612L. Quando alguém serializava uma java.awt.Font em uma versão não podia desserializa-la em outra, sendo que as versões tecnicamente são compatíveis: a não definição explícita do serialVersionUID gerou um bug no Java SE. Como isto foi resolvido? Definiram o serialVersionUID como -1514471214376796190L, que é o valor que seria gerado pela JVM na versão anterior da classe.

Como então devemos proceder para escolher um serialVersionUID apropriado? É muito simples: se essa classe está nascendo neste momento, você pode se dar ao luxo de utilizar um serialVersionUID, como por exemplo:

public class Usuario implements Serializable {
  private static final serialVersionUID = 1L;
  private String login;
}

Porém se você está definindo o serialVersionUID de uma classe já em produção, e sabe que a mudança que está fazendo é compatível com a versão anterior, você deve utilizar o serialVersionUID que seria gerado pela JVM na primeira versão, como foi o caso aqui quando adicionamos o atributo senha na classe Usuario, e também foi o caso da correção do bug da classe java.text.AttributedCharacterIterator.Attribute. Quando você fizer uma alteração onde percebe que o cliente precisará de atualização das classes envolvidas, basta definir um serialVersionUID diferente dos anteriormente utilizados.

Para completar, implementar uma interface que não define métodos (Serializable) e ser forçado a escrever um atributo sem um contrato mais burocrático é um tanto estranho em uma linguagem como o Java. Sem dúvida, se esse mecanismo todo tivesse sido inventado já com a existência de anotações, Serializable seria uma anotação e version um atributo dela, talvez obrigatório, criando algo como @Serializable(version=12345L). Boas serializações e invocações remotas!

Novidades do EJB 3.1 do futuro Java EE 6

Por Nico Steppat em 24/03/08

A especificação EJB 3.0 já está no mercado há quase 2 anos e simplificou bastante o desenvolvimento. O uso de anotações (XML opcional) e POJOs/POJIs são as características principais. EJB 3.0 faz parte da Java EE 5.

Ainda esse ano deve sair o Java EE 6 e, com ele, também uma atualização da especificação do EJB. Vamos ver quais são as novidades/melhorias propostas atualmente no rascunho do EJB 3.1:


Facilidades no desenvolvimento

1) Interfaces locais são opcionais.

Um session bean local pode ficar simples assim:


@Stateless
public class PedidoDAO {

  public void cadastra(Pedido pedido) {
    //código para cadastrar um pedido
  }
}

Não precisa mais da interface e da anotação @Local.

2) EJB’s dentro do war

Será possivel colocar um EJB3 num war, facilitando o packaging. Não é preciso criar um jar e ear separado, tudo pode ser dentro de um unico war. As classes ficam na pasta WEB-INF/classes, e se for usado um ejb-jar.xml, se encontra na pasta WEB-INF.


Novidades

1) Singleton Beans

A ideia é que você pode criar session beans que só existem uma única vez na sua aplicação. Assim é possível criar objetos como o ServletContext na aplicação web. Por exemplo:


@Singleton @ReadOnly
public class ApplicationContextBean implements ApplicationContext {

  @PostConstruct
  public void init() {
    //initializa o contexto da aplicação
  }

  //métodos, por exemplo getAttribute(String chave)
}

Repare a anotação @ReadOnly, declarando o EJB imutável. Também vai existir uma anotação @ReadWrite para singletons beans que podem ser alterados.

O método init() será executado quando o container inicializa a aplicação. Singleton components também serão EJBs e podem ser injetados, o que é uma grande vantagem em relação a ter de ficar preso ao método estático de lookup de um singleton, e uma boa prática de inversão de controle.

2) EJB Timer

Terá uma nova anotação @Schedule referente ao EJB timer para facilitar o agendamento. Por exemplo:



@Schedule(hour=”13”, dayOfMonth=”1”)
public void geraRelatorio() {/* …. */}

executa cada primeiro dia no mes as 13 horas. É um modelo bem superior aos atuais timers do EJB, porém ainda não tão completo quanto aos frameworks open source existentes, como o Quartz.

3) Chamadas assíncronas

Parecidos com Web Services, métodos num session beans também podem ser chamados assincronamente. Para isto serve a anotação @Asynchronous:


@Stateless @Remote(PedidoFacade.class)
public class PedidoFacadeBean implements PedidoFacade{

  @Asynchronous
  public void cadastra(Pedido pedido) {
    //código para cadastrar um pedido
  }

  @Asynchronous
  public Future autoriza(Pedido pedido) {
    //autoriza demora …..
  }
}

O segundo método devolve um object do tipo java.util.concurrent.Future que fornece métodos para verificar se o resultado já chegou.

O lançamento da versão 3.1, que teoricamente ocorrerá até o fim deste ano, deve acelerar ainda mais a adoção da plataforma Java EE 5/6 em relação ao uso das versões antigas J2EE.

Apostila de Java e Orientação a Objetos revisada e ampliada

Por Sérgio Lopes em 12/03/08

FJ-11
Em nossa constante política de manter nossas apostilas atualizadas, estamos disponibilizando hoje a última versão do FJ-11: Java e Orientação a Objetos. Foram várias correções de bugs, atualizações e melhorias no texto (algumas seções foram inclusives reescritas).

Essa apostila já vinha sendo usada aqui na Caelum nos nossos treinamentos há alguns meses. Nesse período, aproveitamos para corrigir bugs e finalizar o material. Entre as novidades, estão algumas discusões sobre design patterns ao longo do curso, atualização para Java 6, novos assuntos (como fluent interfaces) e mais texto. Os capítulos de Threads e Sockets foram reescritos e ampliados. Há também um novo capítulo sobre JARs e bibliotecas, que mostra a famosa ferramenta JFreeChart para desenvolvimento de gráficos em Java.

Muitos exercícios foram reelaborados e estendidos, além de alguns novos que foram incluídos. Há também muitos links para outros materiais, como artigos do nosso blog e outras referências ao longo do texto.

Além disso, a partir dessa versão, estamos usando o Latex (do Donald Knuth) para escrever a apostila e conseguir um resultado final graficamente mais profissional. Aliás já usamos também na apostila de Estruturas de Dados e Algoritmos em Java e logo também nas outras apostilas. (criamos um projeto para ajudar na escrita do latex e gerenciamento das apostilas; mais detalhes em breve)

Acesse a nova versão da apostila na página do treinamento FJ-11. Ou entre em contato conosco para mais informações sobre o treinamento.

Screencast - Hibernate e Concorrência Otimista na Web com VRaptor

Por Fabio Kung em 04/03/08

É com prazer, depois de tanto tempo, que anuncio o segundo screencast da Caelum.

Assim como o screencast anterior (Primeiros Passos para a JPA), o segundo screencast da Caelum vem em dois sabores. Flash vídeo (.flv), que é a mesma tecnologia usada pelo YouTube e pelo Google Video e pode ser assistido pelo navegador. O outro formato disponível é o famoso AVI (DivX), suportado pela grande maioria dos players.

O vídeo trata de um dos recursos pouco explorados no Hibernate: Controle de Concorrência Otimista, para lidar com problemas de edição simultânea (concorrência) nos registros. O fato curioso é que tenho observado em diversos projetos a preferência por Locks Pessimistas, que em grande parte dos casos não são a melhor escolha. Da própria documentação do Hibernate:

The only approach that is consistent with high concurrency and high scalability is optimistic concurrency control with versioning.

(…)

It is not intended that users spend much time worring about locking strategies. Its usually enough to specify an isolation level for the JDBC connections and then simply let the database do all the work.

Como podemos nos beneficiar do controle de concorrência otimista (também conhecido como lock otimista)? Para explorar o assunto, durante o screencast vamos enriquecendo uma aplicação Web existente, adicionando funcionalidades de edição simples de registros e resolvendo o problema de edição simultânea.

A aplicação Web utilizada usa e abusa do VRaptor como controlador MVC e várias dicas sobre o framework são abordadas durante o vídeo. Porém, a mensagem vale para qualquer aplicação Web. Mesmo as que não usam VRaptor.

Como sempre, o screencast mostra apenas uma das alternativas para solução do problema. Fica como lição de casa, testar outras possibildades:

1) Para não fazer o controle transacional dentro do Dao e forçar a checagem de versão (version check), no lugar do transaction.commit() poderia ter sido usado o session.lock(objeto, LockMode.READ). O método lock serve principalmente para a abordagem pessimista, porém LockMode.READ serve justamente para forçar a checagem de versão no caso otimista.

2) No lugar de tirar a entidade do cache de primeiro nível com session.evict(entidade) e consultá-la novamente para ter a versão mais nova do banco, bastaria usar o método session.refresh(entidade) que atualiza a instancia, passando direto pelos caches.

Não deixe de colocar o seu comentário com outras alternativas!

Hibernate e Concorrência Otimista na Web com VRaptor - duração: 48 min

Assista pelo Navegador: Flash Video (.flv): 52 MB, 800×600
Faça o download: AVI, codificação DivX (.avi): 70,1 MB, 800×600
Código Fonte: Código Fonte produzido durante o Screencast. Você vai precisar baixar as dependências (jars) do projeto separadamente. Por favor, leia o arquivo README.txt.