Desafio Java

Processador de arquivos polimórfico com casting seguro

Enunciado

Crie uma hierarquia para processar diferentes tipos de arquivos: Arquivo (abstrata) com método processar(). As subclasses Texto, Imagem e Video devem implementar processar() com mensagens específicas. Texto tem extrairPalavras(), Imagem tem redimensionar(), Video tem reproduzir(). No main, crie uma lista de Arquivo com instâncias variadas. Percorra a lista, chame processar() e, usando instanceof com pattern, chame o método específico de cada tipo. Além disso, demonstre um casting explícito seguro e um casting que lançaria ClassCastException (capture a exceção e imprima uma mensagem).

Requisitos

  • Arquivo é abstrata com método abstrato processar().
  • Cada subclasse implementa processar() e tem um método específico.
  • No main, percorra a lista e use instanceof com pattern para chamar os métodos específicos.
  • Demonstre um casting explícito seguro (após verificação) e um casting inseguro que lança ClassCastException, capturada com try-catch.

Código inicial

import java.util.List;
import java.util.ArrayList;

abstract class Arquivo {
    abstract void processar();
}

// TODO: crie Texto, Imagem e Video

public class Main {
    public static void main(String[] args) {
        List<Arquivo> arquivos = new ArrayList<>();
        // TODO: adicione instâncias
        // TODO: percorra, processe e chame métodos específicos
        // TODO: demonstre casting seguro e inseguro
    }
}

Saída esperada

Processando arquivo de texto.
Extraindo palavras...
Processando imagem.
Redimensionando imagem...
Processando vídeo.
Reproduzindo vídeo...
Casting seguro: Texto
Casting inseguro capturado: class Imagem cannot be cast to class Texto (Imagem and Texto are in unnamed module of loader 'app')
Ver dica

Para o casting inseguro, pegue um objeto Imagem e tente convertê-lo para Texto. Capture ClassCastException e imprima a mensagem da exceção.

Mostrar solução
import java.util.List;
import java.util.ArrayList;

abstract class Arquivo {
    abstract void processar();
}

class Texto extends Arquivo {
    @Override
    void processar() {
        System.out.println("Processando arquivo de texto.");
    }

    void extrairPalavras() {
        System.out.println("Extraindo palavras...");
    }
}

class Imagem extends Arquivo {
    @Override
    void processar() {
        System.out.println("Processando imagem.");
    }

    void redimensionar() {
        System.out.println("Redimensionando imagem...");
    }
}

class Video extends Arquivo {
    @Override
    void processar() {
        System.out.println("Processando vídeo.");
    }

    void reproduzir() {
        System.out.println("Reproduzindo vídeo...");
    }
}

public class Main {
    public static void main(String[] args) {
        List<Arquivo> arquivos = new ArrayList<>();
        arquivos.add(new Texto());
        arquivos.add(new Imagem());
        arquivos.add(new Video());

        for (Arquivo a : arquivos) {
            a.processar(); // despacho dinâmico

            // instanceof com pattern
            if (a instanceof Texto t) {
                t.extrairPalavras();
            } else if (a instanceof Imagem i) {
                i.redimensionar();
            } else if (a instanceof Video v) {
                v.reproduzir();
            }
        }

        // Casting seguro: após verificação
        Arquivo arquivoTexto = new Texto();
        if (arquivoTexto instanceof Texto) {
            Texto t = (Texto) arquivoTexto;
            System.out.println("Casting seguro: " + t.getClass().getSimpleName());
        }

        // Casting inseguro: tentar converter Imagem para Texto
        Arquivo arquivoImagem = new Imagem();
        try {
            Texto t = (Texto) arquivoImagem; // lança ClassCastException
        } catch (ClassCastException e) {
            System.out.println("Casting inseguro capturado: " + e.getMessage());
        }
    }
}

Passo a passo

  1. Definimos Arquivo como classe abstrata com processar(). As subclasses Texto, Imagem e Video implementam processar() com mensagens distintas.
  2. Cada subclasse adiciona um método específico: extrairPalavras(), redimensionar() e reproduzir().
  3. No main, criamos uma lista de Arquivo e adicionamos uma instância de cada subclasse.
  4. No loop, chamamos a.processar(). O despacho dinâmico seleciona a implementação correta de acordo com o tipo real do objeto.
  5. Em seguida, usamos instanceof com pattern para identificar o tipo e chamar o método específico. A variável declarada (ex.: t) já está convertida, evitando cast explícito.
  6. Para demonstrar casting seguro, criamos um Arquivo que é um Texto, verificamos com instanceof e então fazemos (Texto) arquivoTexto. Isso é seguro porque a verificação garante o tipo.
  7. Para o casting inseguro, criamos um Arquivo que é uma Imagem e tentamos convertê-lo para Texto. Isso lança ClassCastException, que capturamos e imprimimos a mensagem.
  8. A mensagem da exceção varia um pouco entre versões da JVM, mas o formato é padronizado.

Por que funciona

O polimorfismo permite tratar todos os arquivos uniformemente através da superclasse. O despacho dinâmico executa o método correto. O instanceof com pattern simplifica a verificação e o acesso a métodos específicos. O casting explícito é necessário quando queremos tratar um objeto como um tipo mais específico, mas só é seguro se o objeto realmente for daquele tipo. Caso contrário, a JVM lança ClassCastException, que pode ser tratada.

Erros comuns

  • Fazer cast sem verificar: Texto t = (Texto) new Imagem(); lança ClassCastException em tempo de execução. Sempre verifique com instanceof antes.
  • Confundir instanceof com getClass(): instanceof considera subclasses, enquanto getClass() == exige tipo exato. Use instanceof para polimorfismo.
  • Esquecer de capturar a exceção: se não capturar, o programa termina abruptamente. Use try-catch para tratar.
  • Ordem errada de instanceof: se uma subclasse estender outra, verifique a mais específica primeiro. Aqui, como são irmãs, não há problema.

Outra forma de resolver

Poderia-se usar o padrão Visitor para evitar instanceof, mas isso adiciona complexidade. Para hierarquias pequenas, instanceof com pattern é aceitável e mais direto.

Saída esperada

Processando arquivo de texto.
Extraindo palavras...
Processando imagem.
Redimensionando imagem...
Processando vídeo.
Reproduzindo vídeo...
Casting seguro: Texto
Casting inseguro capturado: class Imagem cannot be cast to class Texto (Imagem and Texto are in unnamed module of loader 'app')