Como sobrescrever um método que não lança exceção e fazer ele lançar exceção? [RESOLVIDO]

Pessoal, boa tarde!

Gostaria de saber se há alguma técnica para contornar o seguinte problema:

Quero estender a classe DefaultTreeModel e sobrescrever o método abaixo:

public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) { ... }
Só que gostaria de fazer algumas verificações antes de permitir que seja inserido um nó na árvore (JTree). Caso algum critério não seja cumprido, queria disparar uma exceção (que iria para os níveis superiores até chegar ao usuário), impedindo assim que o nó (nosso TreeNode) fosse adicionado à árvore.

Ou seja… após herdar a classe, fazer isso:

@Override
public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) throws MinhaException { ... }

Têm alguma técnica que posso usar para contornar esse problema e poder lançar a exceção para cima? Técnica para adicionar lançamento de exceções em método herdado que originalmente não tinha (mantendo a herança dele) ?

Grato

Sim, basta que sua exceção estenda java.lang.RuntimeException ao invés de estender java.lang.Exception diretamente. Assim o compilador não exigirá que sua exceção seja declarada na assinatura do método.

Ok, vou testar e informo se obter sucesso!

Valeu! Achava que esse problema era insolúvel …

Você pode lançar uma RuntimeException (que não é uma checked exception) que encapsula outra exceção.

Exemplo:

public void teste() { // não há declaração de throws
    try {
        // algum código que pode lançar uma IOException
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Vc precisa de uma classe que É UMA DefaultTreeModel?

Vc poderia usar composição ao inves de herança e ter uma classe X que TEM UM atributo que é uma DefaultTreeModel e vc adiciona / remove nesse cara e a sua classe se preocupa em validar de acordo com as suas regras.

Inclusive sua classe X poderia implementar a interface TreeModel para ser mais compativel com algumas coisas. Lançar RuntimeException quando vc quer validar de forma explicita se algo deu certo me parece um tanto errado.

Vejo outra opção: pense em criar a sua classe de forma imutavel e o insertBlaBlaBla retorna uma nova instancia da sua classe com estas alterações e um método isValid para verificar se o objeto é bacana ou não.

Sim, poderia. Mas não poderia usar como filho do DefaultTreeModel. Teria que adaptar todas as classes para lidar com essa.

Sem dúvida. Mas como vou lidar só com String’s, me parece um esforço desnecessário implementar um TreeModel personalizado só para lançar uma exceção se uma condição não for satisfeita…
Outra abordagem seria a que você apresentou acima. Que daí seria o mais “correto”.

Aí eu quebro o mecanismo de herança. Não quebro? O método é void:

public void insertNodeInto(MutableTreeNode newChild,
                           MutableTreeNode parent,
                           int index)

Acho que uma alternativa melhor é criar uma interface com o método isValid. Daí eu checaria se o model implementa a interface e se o método isValid retorna true. Caso não retorne, eu já saberia que houve duplicação…

Resumindo: Acho o problema simples demais e insignificante para maiores preocupações. Será usado em alguns trechos do programa (3 trechos). Só é para checar se há elementos repetidos no modelo. Se houver, disparar uma exceção. A abordagem do Runtime parece interessante porque somente onde for interessante checar a duplicação e onde eu souber que estou usando o DefaultTreeModel herdado é que vou manipular essa exceção.

[quote=marcobiscaro2112]Você pode lançar uma RuntimeException (que não é uma checked exception) que encapsula outra exceção.

Exemplo:

public void teste() { // não há declaração de throws try { // algum código que pode lançar uma IOException } catch (IOException e) { throw new RuntimeException(e); } } [/quote]

Hoje de manhã vou testar e aviso. A idéia é essa mesma. Lançar “para cima” essa exceção, para ser manipulada nos métodos superiores da pilha, até chegar na interface gráfica (na classe do JFrame, que irá tratar a exceção).

A propósito, se você for usar a abordagem de encapsular a exceção, crie uma classe filha de RuntimeException, que você usará no seu catch. Exemplo:

public class PenseEmUmNomeApropriadoEColoqueAquiException extends RuntimeException {

    public PenseEmUmNomeApropriadoEColoqueAquiException(Exception cause) {
        super(cause);
    }

}

No hora de tratar a exceção, faça um catch só para a sua exceção criada.

Isso é importante pois há pelo menos umas 50 subclasses diferentes de RuntimeException só na API padrão e capturar tudo isso em um catch fica muito genérico.

A abordagem está funcionando. Estou conseguindo capturar a exceção em nível superior.

Estou fazendo isso agora. É melhor mesmo, para evitar capturar outra RuntimeException

[code]public class ExceptionContainer extends RuntimeException {

public ExceptionContainer(Throwable cause) {
    super(cause);
}

}[/code]

Só um detalhe. De que adianta seu TreeModel lançar uma exceção, se seu JTree não está preparado para captura essa mesma exceção?
Se o método não tem esse tipo de coisa no seu contrato, de nada adianta disparar a exception.

[quote=ViniGodoy]Só um detalhe. De que adianta seu TreeModel lançar uma exceção, se seu JTree não está preparado para captura essa mesma exceção?
Se o método não tem esse tipo de coisa no seu contrato, de nada adianta disparar a exception.[/quote]

A idéia é simples. É para capturar a exceção do TreeModel, mas não será o JTree que irá capturar.

As funções CRUD da JTree serão feitas por JButtons e JPopMenu. São nos ActionListeners dos botões e opções do JPopMenu é que vou capturar as exceções do TreeModel. Só vou usar a JTree para obter a instância do TreeModel. Esta instância será justamente uma instância da minha classe herdada da DefaultTreeModel, que encapsula a exceção dentro de uma RuntimeException.
Eu manipulo a instância do TreeModel e verifico se houve a exceção. Se tiver ocorrido, informo o usuário e desfaço as alterações no TreeModel (ou posso impedir de serem salvas).

Deu certo.

Minha aplicação, na interface gráfica, verifica se houve uma “ExceptionContainer” (a extensão que fiz da classe RuntimeException) e trata a exceção contida dentro dela (a que eu lanço da TreeModel, nos níveis mais inferiores, ao inserir e alterar nodos da árvore).
Essa exceção sobe as camadas, sem declarações de throws, sendo capturada só onde interessa e sem eu precisar criar um TreeModel do zero…