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

Erro de cast em enum

o erro: Exception in thread “main” java.lang.ClassCastException: class java.lang.Integer cannot be cast to class br.com.alura.jdbc.modelo.Status (java.lang.Integer is in module java.base of loader ‘bootstrap’; br.com.alura.jdbc.modelo.Status is in unnamed module of loader ‘app’) at br.com.alura.jdbc.dao.ProdutoDAO.trasformarResultSetEmProduto(ProdutoDAO.java:150) at br.com.alura.jdbc.dao.ProdutoDAO.listar(ProdutoDAO.java:66)

o enum:

public enum Status {

    EM_ESTOQUE(1), INDISPONIVEL(5), ENCOMENDADO(10);

    private int valor;

    Status(int valor) {
        this.valor = valor;
    }

    public int getValor() {
        return this.valor;
    }

}

a classe produto: No BD o atributo status é integer.

public class Produto {

    private Integer id;
    private String nome;
    private String descricao;
    private Integer categoriaId;
    private String categoriaNome;
    private Status status;

    public Produto(String nome, String descricao) {
        super();
        this.nome = nome;
        this.descricao = descricao;
    }

    public Produto(Integer id, String nome, String descricao, Status status, Integer categoriaId, String categoriaNome)  {

        this.id = id;
        this.nome = nome;
        this.descricao = descricao;
        this.status = status;
        this.categoriaId = categoriaId;
        this.categoriaNome = categoriaNome;
    }

ProdutoDAO:

public List<Produto> listar() {
        List<Produto> produtos = new ArrayList<Produto>();
        try {
            String sql = "SELECT P.ID, P.NOME, P.DESCRICAO, P.STATUS, C.ID, C.NOME " + "FROM CATEGORIA C "
                    + "INNER JOIN PRODUTO P ON C.ID = P.CATEGORIA_ID";

            try (PreparedStatement pstm = connection.prepareStatement(sql)) { //Para garantir o fechamento dos recursos -> a cláusula try-with-resources
                pstm.execute();

                trasformarResultSetEmProduto(produtos, pstm);
            }
            return produtos;
        } 
        catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private void trasformarResultSetEmProduto(List<Produto> produtos, PreparedStatement pstm) throws SQLException {
        try (ResultSet rst = pstm.getResultSet()) {
            while (rst.next()) {
                Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), (Status) rst.getObject(4), rst.getInt(5), rst.getString(6));
                produtos.add(produto);
            }
        }
    }

Qualquer dica é bem vinda.

Obrigado.

14 respostas

Opa Ricardo, tudo certo?

Então o problema está na hora de trazer do banco, nessa linha aqui:

Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), (Status) rst.getObject(4), rst.getInt(5), rst.getString(6));

Mais especificamente em ((Status) getObject(4)), pois, você salvou como um Integer no banco, ai trazer como um object ou int funciona normal, porém, ao fazer a conversão de Integer para Status é onde esta o problema, afinal são tipos incompatíveis(um é int e o outro uma enum Status), seria a mesma coisa que fazer Status x = (Status) 4;, ele vai nos dizer que são tipos incompatíveis, nem compilaria, vou te dar duas soluções possiveis, uma que você precisa mexer menos e outra que você teria que fazer algumas mudanças.

  1. Primeira solução:

Na enum você adiciona um método assim:

public enum Status {

        EM_ESTOQUE(1), INDISPONIVEL(5), ENCOMENDADO(10);

        private int valor;

        Status(int valor) {
            this.valor = valor;
        }

        public int getValor() {
            return this.valor;
        }

        //Esse metodo que vamos passar um inteiro e devolver um status
        public static Status getBy(Integer valor) {
            return Arrays.stream(Status.values()).filter(status -> status.getValor() == valor)
                    .findFirst().get();
        }
    }

E no DAO você faria assim

Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.getBy(rst.getObject(4)), rst.getInt(5), rst.getString(6));
// Aqui se eu não me engano, assim funcionaria tambem
Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.getBy(rst.getInt(4)), rst.getInt(5), rst.getString(6));
  1. A outra solução possivel seria:

Trocar o campo para String no banco, salvando assim, o valor da enum, fica mais facil de ver na hora de fazer um select diretamente no banco de dados e na hora de recuperar no java é só fazer:

Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.valueOf(rst.getString(4)), rst.getInt(5), rst.getString(6));

Espero que tenha ficado claro, qualquer duvida, só mandar :)

Olá Gabriel,

Troquei o campo para String no banco. Funcionou no DAO, com a linha:

                Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.valueOf(rst.getString(4)), rst.getInt(5), rst.getString(6));

mas agora tenho um botão Salvar onde recebo valores do grid, status é digitado não é um comboBox e também tem problemas de cast Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class java.lang.String cannot be cast to class br.com.alura.jdbc.modelo.Status (java.lang.String is in module java.base of loader 'bootstrap'; br.com.alura.jdbc.modelo.Status is in unnamed module of loader 'app')

    private void alterar() throws SQLException {
        Object objetoDaLinha = (Object) modelo.getValueAt(tabela.getSelectedRow(), tabela.getSelectedColumn());
        if (objetoDaLinha instanceof Integer) {
            Integer id = (Integer) objetoDaLinha;
            String nomeStr = (String) modelo.getValueAt(tabela.getSelectedRow(), 1);
            String nome = nomeStr.toUpperCase();
            String descricao = (String) modelo.getValueAt(tabela.getSelectedRow(), 2);
            Status status =  (Status) modelo.getValueAt(tabela.getSelectedRow(), 3);
            Integer categoriaId =  Integer.parseInt(modelo.getValueAt(tabela.getSelectedRow(), 4).toString());
            // abaixo a variável status já enum Status.
            this.produtoController.alterar(nome, descricao, status, id, categoriaId, null);
            JOptionPane.showMessageDialog(this, "Item alterado com sucesso!");
        } else {
            JOptionPane.showMessageDialog(this, "Por favor, selecionar o ID");
        }
    }

obrigado pela ajuda.

abraço :)

Opa Ricardo, fico muito feliz em ajudar!

Essa parte, seria o mesmo esquema:

Status status = Status.valueOf(modelo.getValueAt(tabela.getSelectedRow(), 3));

Ai vai funcionar e o motivo é o mesmo, porque ele não consegue converter sozinho de String para Status, dessa forma deve funcionar tudo certinho!

Me avisa se funcionar, estarei aguardando :)

eu tentei antes mas nem compila, o erro nesta linha é: The method valueOf(String) in the type Status is not applicable for the arguments (Object).

Opa Ricardo, falha minha.

Faz então

Status.valueOf((String) modelo.getValueAt(tabela.getSelectedRow(), 3));

Assim convertemos de Object para String e depois para o nosso Status.

Olá Gabriel,

funcionou, em parte... rsrsrs

olha só, quando eu altero a coluna status no grid funciona bem mas quando eu altero apenas outro(s) campo(s) do grid sem alterar o status dá o seguinte erro:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class br.com.alura.jdbc.modelo.Status cannot be cast to class java.lang.String (br.com.alura.jdbc.modelo.Status is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap') at br.com.alura.jdbc.view.ProdutoCategoriaFrame2.alterar(ProdutoCategoriaFrame2.java:267)

segue o tercho de código do ProdutoCategoriaFrame2:

    private void alterar() throws SQLException {
        Object objetoDaLinha = (Object) modelo.getValueAt(tabela.getSelectedRow(), tabela.getSelectedColumn());
        if (objetoDaLinha instanceof Integer) {
            Integer id = (Integer) objetoDaLinha;
            String nomeStr = (String) modelo.getValueAt(tabela.getSelectedRow(), 1);
            String nome = nomeStr.toUpperCase();
            String descricao = (String) modelo.getValueAt(tabela.getSelectedRow(), 2);
            Status status = Status.valueOf((String) modelo.getValueAt(tabela.getSelectedRow(), 3));
            Integer categoriaId =  Integer.parseInt(modelo.getValueAt(tabela.getSelectedRow(), 4).toString());
            this.produtoController.alterar(nome, descricao, status, id, categoriaId, null);
            JOptionPane.showMessageDialog(this, "Item alterado com sucesso!");
        } else {
            JOptionPane.showMessageDialog(this, "Por favor, selecionar o ID");
        }
    }

o enum:

package br.com.alura.jdbc.modelo;

public enum Status {

    EM_ESTOQUE, INDISPONIVEL, ENCOMENDADO; 

}

o DAO:

    public void alterar(String nome, String descricao, Status status, Integer id, Integer categoriaId, String categoriaNome) throws SQLException {
        try (PreparedStatement stm = connection.prepareStatement("UPDATE PRODUTO SET NOME = ?, DESCRICAO = ?, STATUS=?, CATEGORIA_ID = ? WHERE ID = ?")) {
            stm.setString(1, nome);
            stm.setString(2, descricao);
            stm.setString(3, status.name().toString());
            stm.setInt(4, categoriaId);
            stm.setInt(5, id);
            stm.execute();
            connection.commit(); // controle da transação
        } 
        catch (SQLException e) {
            e.printStackTrace();
            connection.rollback();
            throw new RuntimeException(e);
        }
    }

e a tela: Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Bizarro, parece que quando você altera, ele já esta reconhecendo que seria uma enum.... poderia me mandar o projeto? git, drive algo do tipo?

Põe bizarro nisso! E tem mais, se você trocar o comando por este abaixo, inverte tudo, ou seja, quando eu altero a coluna status no grid não funciona, dá o mesmo erro mas quando eu altero apenas outro(s) campo(s) do grid sem alterar o status funciona!

        Status status = (Status) modelo.getValueAt(tabela.getSelectedRow(), 3);

segue o link do projeto:

https://drive.google.com/drive/folders/1b5Fgd81xosHvF3SfMJPybVzHmZVBSD-t?usp=sharing

Opa, dando uma olhada aqui, eu chuto que você não precisa fazer isso daqui no ProdutoCategoriaFrame2.java, no metodo preencherTabela() na linha 295:


modelo.addRow(new Object[] { produto.getId(), produto.getNome(), produto.getDescricao(), Status.valueOf(produto.getStatus().name()), produto.getCategoriaId(), produto.getCategoria() });

Porque nesse ponto ele está adicionando o Status como uma enum, e na real, na view pelo menos, podemos lidar como ele sendo uma String, então o código ficaria:


modelo.addRow(new Object[] { produto.getId(), produto.getNome(), produto.getDescricao(), produto.getStatus().name(), produto.getCategoriaId(), produto.getCategoria() });

Opa! Agora sim, funcionou! Valeu!!!!

E aproveitando a ocasião, sabes como transformar a coluna Status no grid em um ComboBox para alteração na classe ProdutoCategoriaFrame2 ?

Então eu nunca mexi muito com JOptionPane, mas pelo que pesquisei caso você fizer algo desse tipo aqui na hora de montar a tabela:

        //ESTE É O CODIGO QUE MONTA A CEDULA CLICAVEL
        TableColumn cedula = tabela.getColumnModel().getColumn(3);
        JComboBox<String> comboBox = new JComboBox<>();
        Arrays.asList(Status.values()).forEach(s -> comboBox.addItem(s.name()));
        cedula.setCellEditor(new DefaultCellEditor(comboBox));

Fiz um código aqui com as coisas que entendi, para tentar te mostrar e você ter uma ideia de como ficaria:


public class TestaComboBox {
    public static void main(String[] args) {
        //MONTAR A TABELA
        JTable tabela = new JTable(5, 5);
        Font font = new Font("Verdana", Font.PLAIN, 12);
        tabela.setFont(font);
        tabela.setRowHeight(30);
        tabela.setBackground(Color.orange);
        tabela.setForeground(Color.white);

        //CRIA A COLUNA CLICAVEL
        TableColumn cedula = tabela.getColumnModel().getColumn(0);

        JComboBox<String> comboBox = new JComboBox<>();
        Arrays.stream(Status.values()).forEach(stat -> comboBox.addItem(stat.name()));

        cedula.setCellEditor(new DefaultCellEditor(comboBox));

        //MONTA A JANELA QUE IRA ABRIR
        JFrame frame = new JFrame();
        frame.setSize(800, 400);
        frame.add(new JScrollPane(tabela));
        frame.setVisible(true);
    }

    public enum Status {
        EM_ESTOQUE, INDISPONIVEL, ENCOMENDADO;
    }
}

Roda esse código, tenta entender o que eu fiz e tenta adicionar em seu projeto, você já tem a tabela e o frame, basicamente seria acrescentar a parte que monta a cédula com as opções de acordo com a Enum.

Tem um guia, na documentação da oracle que é show tambem.

Espero ter ajudado, fico feliz que tenha conseguido! :)

Olá Gabriel, implementei as sugestões que me passaste para transformar a coluna Status(enum) em ComboBox e consegui, funcionou tudo certinho.

Aí, pensei, vou aproveitar tudo que aprendi e vou fazer o mesmo com a coluna Nome da Categoria, baseada na tabela Categoria.

Aconteceu o mesmo erro que com Status, quando clico/altero a coluna Nome da Categoria e não altero as outras colunas funciona mas quando altero outra coluna e não altero a coluna Nome da Categoria dá o erro:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class java.lang.String cannot be cast to class br.com.alura.jdbc.modelo.Categoria (java.lang.String is in module java.base of loader 'bootstrap'; br.com.alura.jdbc.modelo.Categoria is in unnamed module of loader 'app') at br.com.alura.jdbc.view.ProdutoCategoriaFrame2.alterar(ProdutoCategoriaFrame2.java:270)

mas como é difícil esse negócio do cast... hehehe!

segue o link do projeto já alterado.

abração e obrigado pela ajuda.

solução!

Opa Ricardo, a categoria é um pouco diferente...

Você tem algumas opções nesse caso, vou te dar a mais simples e explicar as outras.

Em ProdutoCategoriaFrame2, no método prencherTabela(), na linha 301, você pode alterar o código para:

    private void preencherTabela() {
        //ESTE É O CODIGO QUE MONTA A CEDULA CLICAVEL DO STATUS NO GRID
        TableColumn cedula = tabela.getColumnModel().getColumn(3);
        JComboBox<String> comboBox = new JComboBox<>();
        Arrays.asList(Status.values()).forEach(s -> comboBox.addItem(s.name()));
        cedula.setCellEditor(new DefaultCellEditor(comboBox));

        //ESTE É O CODIGO QUE MONTA A CEDULA CLICAVEL DA CATEGORIA NO GRID       
        TableColumn cedula2 = tabela.getColumnModel().getColumn(4);
        comboGridCategoria = new JComboBox<Categoria>();
        List<Categoria> categorias = this.listarCategoria();
        for (Categoria categoria : categorias) {
            comboGridCategoria.addItem(categoria);
        }
        cedula2.setCellEditor(new DefaultCellEditor(comboGridCategoria));

        List<Produto> produtos = listarProduto();
        try {
            for (Produto produto : produtos) {
                //Adicionei essa linha para carregar a categoria ou você pode carregar do BD
                Categoria categoriaEncontrada = categorias.stream().filter(categoria -> produto.getCategoriaNome().equalsIgnoreCase(categoria.getNome())).findFirst().get();
                modelo.addRow(new Object[] { produto.getId(), produto.getNome(), produto.getDescricao(), produto.getStatus().name(), categoriaEncontrada });
            }
        } catch (Exception e) {
            throw e;
        }    
    }

Isso deve fazer funcionar já, tem algumas outras soluções, mas acho que seria a menos dolorida de fazer.

Espero que funcione!

Sensacional! Funcionou!

Gabriel, quero te agradecer pelo apoio, pelo suporte e pela paciência!

Valeu muito!

abraço.