05 fevereiro 2009

SCJP - 02 - Métodos Sobrescritos (Overriding)

Novamente, nós temos o costume de nos restringirmos a dadas regras (e apenas elas) da linguagem. No caso da sobrescrita (que é diferente da sobrecarga) de métodos, o que nós estamos acostumados a ver são métodos, tanto na classe base como na derivada, exatamente com a mesma assinatura.

class Base {
    public String teste(){
        return "teste base";
    }
}

class Derivada extends Base {
    public String teste(){
        return "teste derivada";
    }
}

Como vocês podem ver, neste caso, a classe derivada mantém o tipo de retorno, o modificador de acesso e a lista de parâmetros do método sobrescrito. Mas sobrescrita de métodos não se restringe a apenas isso. Existem diversas outras possibilidades, que podem ser analisadas no livro SCJP Sun Certified Programmer for Java 6 Exam.

  1. A quantidade e a ordem de parâmetros do método sobrescrito deve ser exatamente a mesma descrita na classe base, do contrário, você terá um método sobrecarregado.

  2. O tipo de retorno precisa ser o mesmo, ou um subtipo do tipo de retorno declarado no método a ser sobrescrito.

  3. O nível de acesso não pode ser mais restritivo que o descrito no método a ser sobrescrito. O código abaixo exemplifica a situação:




    class Base {
        public String teste() {
            return "classe base";
        }
    }
    
    class Derivada extends Base {
        protected String teste() { 
            return "classe derivada";
        }
    }
    
    public class Principal {
        public static void main(String[] args) {
            Derivada derivada = new Derivada();
            Base base = derivada;
    
            System.out.println(
                base.teste());
    
            System.out.println(
                derivada.teste());
    
        }
    }
    

    Notou o porquê disso não ser possível? Bem, vamos por partes. Nós dissemos que não é possível sobrescrever um método usando um modificador de acesso mais restrito. Pense no seguinte. Se eu tenho um método na classe base com um dado modificador de acesso (digamos, public), todas as referências sabem como e onde podem acessá-lo (o  método). E nós também sabemos que dada uma referência, a ela podem ser atribuídos objetos do mesmo tipo, ou de subtipos do tipo declarado pela referência. Até aí tudo bem?

    Tomando isso por base, pense no seguinte: tenha uma referência do tipo base, atribuindo a ela um objeto do tipo derivado. Talvez numa situação semelhante ao código descrito neste item fosse possível, porque se duas classes estão no mesmo pacote, é possivel chamar métodos com nivel de acesso default, protected e private. Mas e quando não for? A referência da classe base espera acessar o método sem problemas, mas quando a máquina virtual invocar o método, notará o problema... FAIL. Assim, pra garantir que SEMPRE seja possível acessar o método sobrescrito da classe derivada, este tipo de situação é barrada em tempo de compilação, com um erro semelhante ao abaixo:





  4. O nível de acesso pode ser menos restritivo que o descrito no método a ser sobrescrito. O código abaixo exemplifica a situação:




    class Base {
        protected String teste() {
            return "classe base";
        }
    }
    
    
    class Derivada extends Base {
        public String teste() { 
            return "classe derivada";
        }
    }
    
    public class Principal {
        public static void main(String[] args) {
            Derivada derivada = new Derivada();
            Base base = derivada;
    
            System.out.println(
                base.teste());
    
            System.out.println(
                derivada.teste());
    
    
        }
    }
    

    Á saída do programa abaixo é mostrada a seguir:

    De forma oposta à explicada acima, uma referência à classe base espera poder acessar um método da forma especificada pelo modificador de acesso. O método sobrescrito na classe derivada oferece um nivel de acesso maior (com certeza, englobando o nivel de acesso descrito pelo método na classe base), o que não é um problema.






  5. Métodos de instância só podem ser sobrescrito se eles forem herdados pela subclasse. Isso quer dizer que, por exemplo, métodos com modificadores de acesso private e final não podem ser sobrescritos. Ohhhh tiiiooo! Mas por quê? Métodos e variáveis private não são herdados pela classe. Simplesmente se tornam indisponíveis. Além disso, definir o modificador de acesso final num método, como vimos antes, impossibilita que subclasses sobrescrevam o método.

  6. Métodos sobrescritos podem lançar unchecked exceptions (exceções checadas em tempo de execução, aquelas que herdam de RuntimeException), independente deles terem sido declarados no método da classe base. Isso merece uma pausa. Como assim, Bial? Exceções não checadas em tempo de execução são aquelas do tipo IndexOutOfBoundsException (que ocorre quando você itera sobre um array, usando um valor de índice maior que o tamanho do array, por exemplo), ou NullPointerException (dispensa explicações). O código abaixo é um exemplo disso:




    class Base {
        public String teste(int i) {
            return "classe base";
        }
    }
    
    class Derivada extends Base {
        public String teste(int i) 
        throws IndexOutOfBoundsException{
            if (i > 0) {
                return "classe derivada";
            } else {
                throw new 
                  IndexOutOfBoundsException();
            }
        }
    }
    

    Tá, eu sei que o código acima não faz sentido nenhum, mas é só um exemplo ;)

  7. Métodos sobrescritos não podem lançar checked exceptions (todas as outras que não herdam de RuntimeException) diferentes ou mais gerais que aquelas declaradas no método original da classe base. Por exemplo: se o método da classe base declarar uma exceção do tipo ParseException (geralmente lançada quando não se consegue transformar uma String numa data, usando o SimpleDateFormat, por exemplo), o método sobrescrito da classe derivada não vai poder declarar um NoSuchMethodException, um Exception ou qualquer outra exceção, a menos que seja derivada de ParseException.

  8. O método sobrescrito da classe derivada pode lançar exceções mais específicas ou ainda menos exceções que o método original da classe base. Por exceções mais específicas, entenda como uma subclasse da exceção especificada no método da classe base. O método sobrescrito pode ainda não lançar uma exceção sequer. O motivo disso é: se você está sobrescrevendo um método, você pode alterar o comportamento dele, de modo que tais exceções sejam desnecessárias. Um exemplo de código, pra entender melhor.




    import java.io.*;
    import java.sql.*;
    
    class Base {
        public String teste() throws 
        IOException, SQLException{
            return "classe base";
        }
    }
    
    
    class Derivada extends Base {
        public String teste() 
        throws FileNotFoundException{ 
            return "classe derivada";
        }
    }
    
    public class Principal {
        public static void main(String[] args) 
        throws Exception {
            Derivada derivada = new Derivada();
            Base base = derivada;
    
            System.out.println(
                base.teste());
    
            System.out.println(
                derivada.teste());
    
        }
    }
    

    Veja que FileNotFoundException é um subtipo de IOException ;)

É bom estar atento a coisas como essas, porque na hora do exame, não vai haver um eclipse instalado, com aquele ícone vermelho na linha da declaração do método, indicando o porquê ele não pode ser sobrescrito :)

Creative Commons License
Esta obra está licenciada sob uma Licença Creative Commons.
Comentários
1 Comentários

1 comments:

Anônimo disse...

Noooossaaaaaa, muitooo boa sua explicação!!!!
Todas as explicações anteriores a sua eu nao estava conseguindo entendder nadaaaa....

Postar um comentário

Regras são chatas, mas...

- Seu comentário precisa ter relação com o assunto do post;
- Em hipótese alguma faça propaganda de outros blogs ou sites;
- Não inclua links desnecessários no conteúdo do seu comentário;
- Se quiser deixar sua URL, comente usando a opção OpenID;
- CAIXA ALTA, miguxês ou erros de ortografia não serão tolerados;
- Ofensas pessoais, ameaças e xingamentos não são permitidos;