Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Gerando hashCode com IntelliJ IDEA

No vídeo, o Eclipse gera o seguinte código:

@Override
    public int hashCode(){
        final int prime = 31;
        int result = 1;
        long temp;
        temp = Double.doubleToLongBits(saldo);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
        }

Eu não entendi exatamente o que esse código faz, linha por linha, nem parece ser esse o foco, já que o professor também não explica, só gera e usa, mas o problema maior é que, usando o IntelliJ, ele gera apenas esse código:

@Override
    public int hashCode() {

        return Objects.hash(saldo);
    }

A intenção é fazer com que contas com o mesmo saldo sejam consideradas como duplicadas e uma delas será "ignorada". Com o código gerado pelo IntelliJ isso não acontece.

Depois, mesmo copiando o código que o Eclipse gera no IntelliJ, não funciona, a saída continua sendo 2 para o tamanho da lista Contas:

package com.company.br.com.caelum.fj11.programa;

import com.company.br.com.caelum.fj11.modelo.Conta;
import com.company.br.com.caelum.fj11.modelo.ContaCorrente;

import java.util.*;

public class TestaColecoes {

    public static void main(String[] args) {
        Set<String> nomes = new HashSet<String>(); 
        nomes.add("Mauricio");
        nomes.add("Guilherme");
        nomes.add("Guilherme");

        System.out.println(nomes.size());


        Set<Conta> contas = new HashSet<Conta>();
        Conta c1 = new ContaCorrente();
        c1.deposita(500);
        Conta c2 = new ContaCorrente();
        c2.deposita(500);
        contas.add(c1);
        contas.add(c1);
        contas.add(c2);

        System.out.println(contas.size());
    }
}

Saída:

/Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=56137:/Applications/IntelliJ IDEA CE.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Users/brunasantos/IdeaProjects/BancoAntigo/out/production/BancoAntigo com.company.br.com.caelum.fj11.programa.TestaColecoes
2
2

Process finished with exit code 0
  • Tem como gerar a hashCode com esse código todo que o Eclipse gera no IntelliJ?
  • Por que minha saída continua sendo "2", mesmo usando o código que o Eclipse gera?

Atualização da dúvida: Vi também que o Eclipse gera o método equals, coisa que o IntelliJ também não fez.

Quando eu inseri o código do equals

public boolean equals(Object obj){
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        Conta outraConta = (Conta) obj;

        if(Double.doubleToLongBits(saldo) != Double.doubleToLongBits(outraConta.saldo));
        return false;
    }

Apresenta um erro de que o equals já foi definido, como resolvo isso? Meu código completo segue no comentário, não cabe aqui.

Obrigada!

6 respostas

Código completo:

package com.company.br.com.caelum.fj11.modelo;

import com.company.br.com.caelum.fj11.excecao.SaldoInsuficienteException;
import com.company.br.com.caelum.fj11.excecao.ValorInvalidoException;

import java.util.Objects;

public abstract class Conta implements Comparable<Conta>{

    protected int numero;
    private String dono;
    protected double saldo;
    private double limite;
    private Cliente titular = new Cliente();
    private static int totalDeContas = 0;
    private static int numeroIncremental = 1;

    public Conta(){
        this.numero = numeroIncremental;
        numeroIncremental++;
        totalDeContas++;
    }

    public Conta(double limite) {

        this(limite, 0);
    }

    public Conta(double limite, double saldoInicial) {
        this.limite = limite;
        this.saldo = saldoInicial;
        this.numero = numeroIncremental;
        numeroIncremental++;
        totalDeContas++;
    }

    public String toString() {

        return "Conta com saldo R$ " + this.saldo;
    }

    public boolean equals (Object obj){
        Conta outraConta = (Conta) obj;
        return this.numero == outraConta.numero && this.titular.equals(outraConta.titular);
    }

    @Override
    public int hashCode() {

        return Objects.hash(saldo);
    }

    public boolean equals(Object obj){
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        Conta outraConta = (Conta) obj;

        if(Double.doubleToLongBits(saldo) != Double.doubleToLongBits(outraConta.saldo));
        return false;
    }

    public double getLimite() {
        return limite;
    }

    public void setLimite(double limite) {
        this.limite = limite;
    }

    public Cliente getTitular() {
        return titular;
    }

    public void setTitular(Cliente titular) {
        this.titular = titular;
    }

    public int getNumero() {

        return this.numero;

    }

    public void setNumero(int numero) {
        this.numero = numero;
    }

    public String getDono() {
        return dono;
    }

    public void setDono(String dono) {
        this.dono = dono;
    }

    public static int getTotalDeContas() {
        return totalDeContas;
    }

    public static int getNumeroIncremental() {
        return numeroIncremental;
    }

    public void saca(double valor) throws Exception { //meio obscuro
        if (this.saldo >= valor) {
            this.saldo -= valor;
        } else {
            throw new SaldoInsuficienteException(saldo);
            //throw new RuntimeException();
        }
    }

    public void deposita(double quantidade) throws ValorInvalidoException {//confuso -> Checked Exception
        if(quantidade < 0) {
            //throw new IllegalArgumentException("Valor inválido, tente outra vez!");
            throw new ValorInvalidoException(quantidade);
        }else {
            this.saldo += quantidade;
        }
    }

    public double getSaldo() {
        return saldo;
    }

    public abstract void atualiza(double taxa);

    public boolean transfere(Conta destino, double valor) throws Exception {
        boolean retirou;
        if(this.saldo >= valor) {
            this.saldo -= valor;
            destino.deposita(valor);
            return retirou = true;
        } else {
            return false;
        }
    }

    @Override
    public int compareTo(Conta outra) {
        if(this.numero < outra.numero) return -1;
        if(this.numero > outra.numero) return 1;
        return 0;
    }
}
solução!

Boa noite, Bruna! Como vai?

Vc está obtendo o erro de que o equals já foi definido pq sua classe Conta tem dois métodos equals()!

    public boolean equals (Object obj){
        Conta outraConta = (Conta) obj;
        return this.numero == outraConta.numero && this.titular.equals(outraConta.titular);
    }

    public boolean equals(Object obj){
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        Conta outraConta = (Conta) obj;

        if(Double.doubleToLongBits(saldo) != Double.doubleToLongBits(outraConta.saldo));
        return false;
    }

O método equals() é o responsável por indicar como objetos da classe (nesse caso da classe Conta) devem ser comparados! Veja que o primeiro método equals() é o que indicará que uma conta é igual a outra se o número e o titular delas forem iguais, portanto, provavelmente é esse que vc quer usar. Então para resolver o erro que vc descreveu, basta apagar o segundo método equals().

No entanto, veja que no seu primeiro equals() vc faz this.titular.equals(outraConta.titular), então na sua classe Cliente vc tbm deverá implementar o método equals() indicando como deseja comparar um cliente com outro.

Outra coisa, se a intenção é

fazer com que contas com o mesmo saldo sejam consideradas como duplicadas

Então o seu método equals() deve ficar assim:

    public boolean equals(Object obj) {
        Conta outraConta = (Conta) obj;
        return this.saldo == outraConta.saldo;
    }

Veja aí se funciona! Me dá um retorno aqui e qualquer coisa eu continuo a te ajudar!

Grande abraço e bons estudos!

HashCode é a forma de gerar um número único que representa aquilo (o objeto). quanto mais "complicado" e "único" um número, maiores as chances de continuar único.

Como "bons modos", toda vez que você quer comparar dois objetos do mesmo tipo referência ou do mesmo tipo, você sempre cria um HashCode novo.

Não se esqueça: sem a anotação de @Override, o equals não funciona porque ele é uma implementação padrão da classe Object (tal qual como toString).

@Override
public boolean equals (Object obj) {

Gabriel e Gustavo,

Bom dia! Obrigada aos dois pelas respostas, queria marcar as duas como solução, pois elas complementam-se.

O que eu percebi é que, diferente do Eclipse, o IntelliJ não pergunta se desejo substituir o método equals já existente, acaba criando outro, mas agora entendi que não posso ter dois e o porquê.

Gabriel, seu código funciona sim e também consegui que o próprio IntelliJ criasse o código quando apaguei o método equals anterior. Dá um código bem diferente do seu, o seu é mais fácil de entender, mas ficou tudo funcionando, obrigada!

Gustavo, eu entendi que sem o hash o equals não funciona, fiz um teste para verificar. Mas tenho uma dúvida, quando você diz sem a anotação de @Override é que precisa constar essa anotação? Porque eu comentei a anotação e funcionou da mesma forma. Entendi errado?

O código ficou assim:

//@Override
    public int hashCode() {

        return Objects.hash(saldo);
    }

    //@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Conta conta = (Conta) o;
        System.out.println("Estou usando equals");
        return Double.compare(conta.saldo, saldo) == 0;
    }

O método que testa:

package com.company.br.com.caelum.fj11.programa;

import com.company.br.com.caelum.fj11.modelo.Conta;
import com.company.br.com.caelum.fj11.modelo.ContaCorrente;

import java.util.*;

public class TestaColecoes {

    public static void main(String[] args) {
    Set<String> nomes = new HashSet<String>(); //impede que Strings iguais fiquem repetidas
        nomes.add("Mauricio");
        nomes.add("Guilherme");
        nomes.add("Guilherme");

        System.out.println(nomes.size());


        Set<Conta> contas = new HashSet<Conta>();
        Conta c1 = new ContaCorrente();
        c1.deposita(500);
        Conta c2 = new ContaCorrente();
        c2.deposita(500);
        contas.add(c1);
        contas.add(c1);
        contas.add(c2);

        System.out.println(contas.size());
  }
}

E a saída:

/Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=62108:/Applications/IntelliJ IDEA CE.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Users/brunasantos/IdeaProjects/BancoAntigo/out/production/BancoAntigo com.company.br.com.caelum.fj11.programa.TestaColecoes
2
Estou usando equals
1

Process finished with exit code 0

Obrigada!

Bruna, de fato, não é obrigatório você adicionar o @Override em momento algum do código. Mas é extremamente recomendado você adicionar essa anotação sempre que você for sobrescrever um método da superclasse/implementar uma interface.

1- deixa o código de fácil compreensão para o humano 2- se você está dando implementando uma Interface, o compilador vai pegar um possível erro nessa implementação (digitou o nome errado, faltou algum detalhe do método, etc).

Você alterou métodos da superclasse objeto ao mudar o toString e equals/hash. @Override é a forma de deixar para o próximo humano que você está forçando do jeito que você quer.

Entendi, Gustavo, muito obrigada!