Erros irritantes cometidos por programadores Java, parte 1

No post de hoje, eu irei falar sobre alguns erros muito comuns cometidos por programadores Java ao codificarem seus programas. São erros estúpidos que costumam me irritar bastante quando vejo um no meio do código. São erros que podem provocar desde o desperdício de recursos (memória, principalmente) até comportamentos inesperados que podem resultar em erros nos aplicativos.

A intenção era de colocar outros erros além desses seis, mas o post acabou ficando muito comprido e os outros erros ficarão para futuros posts. Então, acabei por colocar neste post apenas erros relacionados a comparações de igualdade.

Este post (e os próximos com a mesma temática deste) tem por finalidade alertar programadores (tantos os iniciantes como os experientes, que, como todo ser humano, também cometem erros) para que estes deixem de cometer esses erros. Seguem os erros abaixo:

1) Comparar Strings com dois sinais de igual (==): Esse é um dos erros muito comuns cometidos pelos programadores Java. Comparar Strings com == presumindo que comparações de Strings iguais (com a mesma sequencia de caracteres) feitas dessa maneira sempre retornará true (ou seja, que a condição é verdadeira) pode provocar erros de lógica, pois a comparação pode muito bem retornar false (ou seja, que a condição é falsa). Isto porque comparar Strings com dois sinais de igual (ou melhor, comparar qualquer objeto com dois sinais de igual) significa que você está verificando se as duas referências aos objetos apontam para o mesmo objeto, ou seja, você está verificando se os dois objetos (as duas Strings, neste caso) são, na verdade, o mesmo objeto. Como novas instâncias da classe String podem ser criadas com o comando new (assim como qualquer instâncias de qualquer classe), e o comando new sempre resulta na criação de um novo objeto, e como não dá para garantir que uma String vinda vai saber de onde é uma String declarada com um literal de String (uma String que é declarada com seu valor entre aspas) ou resultante de um comando new, então não é garantido que uma comparação com dois sinais de igual irá retornar true caso as Strings sejam iguais.

Ao invés de comparar Strings com dois sinais de igual, o correto é comparar as Strings com o método equals. Basicamente, pega-se uma das Strings e chama-se o método equals nessa String, passando como parâmetro a outra String, que vai ser comparada com a primeira a fim de verificar se ela é igual à outra. Caso as Strings sejam iguais, ainda que sejam objetos diferentes, o método equals retornará true.

Objetos String até podem ser comparados com dois sinais de igual, desde que tanto a String da esquerda do == quanto a String da direita, ou devem ser literais de String, ou devem ser String resultantes de uma chamada do método intern de outra String, ou então deve ser uma referência cujo valor foi sabidamente atribuído (e sem ter jeito de não ser) ou a um literal de String, ou a uma String resultante de uma chamada do método intern de outra String. O método intern sempre retorna uma String do pool de Strings da máquina virtual igual à String de onde o método foi chamado, ou a mesma String, caso ela seja uma String do pool de Strings.

2) Comparar números encapsulados por objetos numéricos wrappers com dois sinais de igual (==): É o mesmo problema de comparar Strings com dois sinais de igual, já que os objetos podem ser diferentes. Com números inteiros até o 128 não há problema, pois números até o 128 são armazenados num pool de inteiros pela máquina virtual (poucos programadores sabem disso), entretanto, como não é garantido que um objeto de uma classe wrapper de um tipo numérico representa um número menor ou igual a 128, é melhor não arriscar e fazer a comparação com o método equals. Ou então pegar os valores resultantes de chamadas aos métodos da própria classe wrapper que retornam o número propriamente dito (armazenado em um tipo primitivo, não em um objeto), como o método intValue, que retorna um número inteiro de 32 bits (o popular int), e fazer a comparação com dois sinais de igual, pois daí você estará comparando os valores numéricos em si, que não são objetos. Utilizar os métodos que retornam números em tipos primitivos permite que sejam feitas comparações entre objetos wrappers de tipos diferentes, como Integer e Long, por exemplo.

3) Comparar dois objetos de uma classe implementada pelo programador com o método equals, mas sem sobrescrever o mesmo: O método equals pode ser chamado a partir de qualquer objeto e tem por finalidade verificar se o conteúdo do objeto em questão é igual ao conteúdo do objeto passado como parâmetro. Em classes escritas pelo programador que estão sujeitas a terem seus objetos comparados entre si a fim de verificar se são iguais, o programador deve sempre sobrescrever o método equals, implementando um algoritmo que efetue a comparação dos atributos da classe em questão, verificando se o conteúdo dos atributos é igual ao conteúdo dos atributos do objeto a comparar.

Caso o programador não sobrescreva o método equals padrão que é herdado da classe Object (a classe mãe de todas as outras classes Java, que possui métodos comuns a todas elas), a comparação é feita com a implementação padrão provida pela classe Object, que… Adivinhem! Compara os objetos com dois sinais de igual! Ou seja, compara se os dois objetos são, na verdade, o mesmo objeto. Aí, caso os objetos tenham os mesmos valores em seus atributos, a comparação poderá retornar false, caso os objetos sejam diferentes.

4) Sobrescrever o método equals, mas não sobrescrever o método hashCode: caso desses métodos seja sobrescrito, o outro também deve ser, pois o contrato de ambos os métodos (não irei explicar aqui o que é um contrato de um método, caso não saiba, acho que o Google pode lhe ajudar, assim como me ajuda quando eu não sei algumas coisas) diz que, caso a comparação entre dois objetos pelo método equals retorne true, o retorno do método hashCode de ambos deve ser igual. Atenção: isto não significa que objetos com valores diferentes não devem ter o mesmo hashCode. sobrescrever o método equals e não sobrescrever o método hashCode pode resultar em resultados imprevisíveis caso o objeto em questão seja utilizado em alguma classe que chame ambos os métodos internamente, como as classes do Java Collections Framework, que são as populares coleções do Java (classes ArrayList, LinkedList, HashSet, HashMap, etc).

5) Armazenar um objeto de uma classe implementada pelo programador em uma coleção ou um Map sem implementar os métodos equals e hashCode dessa classe: as classes do Java Collections Framework fazem uso massivo desses dois métodos a fim de saber se um objeto possui os mesmos valores de outro, principalmente as classes que implementam a interface Map (HashMap, TreeMap, LinkedHashMap, ConcurrentHashMap, etc), que usam o valor retornado pelo método hashCode para posicionar o objeto dentro do mapa de objetos que foram postos dentro do Map. Caso um dos dois métodos deixe de ser implementado, um objeto da classe em questão pode se perder dentro do Map (entrar e não sair mais), já que, na hora de recuperar o objeto, é verificado o retorno do método hashCode a fim de saber onde o objeto está em relação ao mapa, além de eventualmente (dependendo das implementações dos Maps e de quais métodos forem chamados) haver comparações com o método equals a fim de saber se um determinado objeto é igual a outro. Isso vale para todas as classes do Java Collections Framework, mesmo para Lists (ArrayList, LinkedList, Vector, etc), uma vez que alguns métodos fazem uso do equals e do hashCode em seus objetos armazenados, como o método contains, que verifica se um objeto está contido na lista comparando cada um com o método equals.

6) Comparar String ou números encapsulados por objetos numéricos wrappers ou qualquer objeto usando um ponto de exclamação e um sinal de igual (!=): um ponto de exclamação e um sinal de igual é o contrário de dois sinais de igual, ou seja, verifica se dois objetos (ou dois tipos primitivos) são diferentes. Do mesmo modo que comparações com == presumindo que isto faz comparar se o valor dos objetos é igual podem zicar com o funcionamento de um programa, comparações com != presumindo que isto faz comparar se o valor dos objetos é diferente também não é uma boa ideia, apesar de que objetos cujas comparações entre si com o método equals retornem false são sempre objetos diferentes. Isto porque, do mesmo modo, presume-se que comparações com != entre objetos iguais retornem false, quando podem acabar retornando true na hipótese dos objetos serem diferentes. Neste caso, usa-se o método equals com um ponto de exclamação à esquerda do objeto cujo método equals está sendo chamado. Isto faz com que o retorno do método equals seja “invertido”, ou seja, faz retornar false quando o método retorna true e vice-versa. Aliás, o ponto de exclamação à esquerda de qualquer método que retorne um valor boolean (true ou false) sempre “inverte” o retorno desse método.

Bom, por hoje é só, mas futuramente, escreverei melo menos mais um post acerca de erros cometidos por programadores Java, que será a continuação deste aqui.

Anúncios

5 Respostas to “Erros irritantes cometidos por programadores Java, parte 1”

  1. Gean Carlos Says:

    Ola boa noite estou comparando uma string de uma bd mysql em java pelo equals tudo bem compara em if( string.equal (c.result set (“”) beleza. o problema é quando quero o contrário ou seja o else para mandar uma mensagem para o usuario por exemplo o produto nao existe cadastre o produto este else nao está funcionando tens alguma dica… obrigado

  2. O problema está na comparação ou na implementação da mensagem de erro? Caso a resposta seja a segunda opção, aí depende de como o sistema é implementado, se é com interface gráfica usando a API Swing, se é uma aplicação web usando JSF, ou usando JSTL, ou Struts, ou se é uma aplicação em modo texto… Caso seja em modo texto, poderá simplesmente usar um System.out.println(“O produto não existe”), caso seja num sistema com interface Swing, poderá usar JOptionPane.showMessageDialog(this, “O produto não existe”) ou outro método semelhante da classe JOptionPane. Isso depende muito de como o sistema é implementado.

  3. O Informante Says:

    Nossa Alan, li o JOptionPane.showMessageDialog lembrei muito da minha epoca de faculdade kkkkk…assim como println eh o mesmo comando porem modo texto (assim como voce informou), e pode ser usado tambem por C, Delphi Etc…usavamos o println ao inves de imprimir.texto nas aulas de logica de programacao.

    Saudades dessa epoca, legal seu post, parabens.

  4. Francielle da Luz Says:

    Achei bobagem pra falar a verdade, entendo o que você quiz dizer, porém acho que foi mais um artigo escrito em forma de desabafo que gira em torno de um unico fato -> de o == ser confundido com o .equals pois a maioria das outras linguagens não tem .equals.
    Meu comentario pessoal sobre erros irritantes que eu mesma já cometi:
    Evite ao maximo repetir-se, isso é meio automatico acho que em se tratando da maioria dos programadores desatentos, o ctrl+c,ctrl+v, quando passamos por problemas similares e resolvemos que a melhor maneira de resolver é copiar o bloco de código que foi pensado lah em cima, na verdade estamos deixando o acoplamento alto e correndo grande risco de quando a regra lá em cima for mudado tenhamos de percorrer o código refazendo a mesma coisa varias vezes, não caia nessa armadilha sua vida será + colorida sem o ctrl+c,ctrl+v . Francielle Luz Curitiba :D

  5. […] FONTE: https://irmaodamara.wordpress.com/2011/09/03/erros-irritantes-cometidos-p… […]

Comente este post!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s