Sobrescrita e Sobrecarga de métodos em Java
A sobrescrita e a sobrecarga de métodos são comumente utilizadas no dia a dia do desenvolvedor Java. Tendo este cenário, é essencial que o desenvolvedor saiba como utilizar este recurso e quais as regras para realizar tal abordagem.
Neste artigo veremos dois dos conceitos mais utilizados na programação orientada a objetos, a sobrescrita e sobrecarga de métodos. Iremos explorar como e onde é possível aplicá-los de forma correta e consciente, deixando evidente quando um método é realmente sobrescrito ou sobrecarregado. Passaremos também por exemplos práticos, como a sobrescrita do método toString() e a sobrecarga de construtores.
A sobrescrita e a sobrecarga de métodos acompanham o Java desde o seu início. Estas são características permanentes desta linguagem orientada a objetos, seja ela utilizada em um ambiente de desenvolvimento móvel (Java ME), desktop (Java SE) ou corporativo (Java EE). No entanto, mesmo com tal importância, existem muitos questionamentos que rondam esses dois conceitos.
É bastante comum, principalmente para os iniciantes na linguagem, estranharem a existência de métodos com mesmo nome declarados em uma mesma classe, situação que certamente geraria erro em algumas linguagens de programação.
Outro ponto que gera bastante dúvida, até mesmo naqueles que já possuem alguma vivência com a linguagem, é saber diferenciar um método sobrescrito de um método sobrecarregado, ou até mesmo saber qual versão do método será executada.
Apesar destas frequentes dúvidas que envolvem os conceitos de sobrescrita e sobrecarga, a utilização destes recursos é algo bastante comum, e muitas vezes indispensável. Pensando nisso, neste artigo eliminaremos essas dúvidas mostrando como sobrescrever e sobrecarregar métodos, onde podemos aplicar e quais as regras envolvidas que devem ser seguidas.
A sobrescrita (ou override) está diretamente relacionada à orientação a objetos, mais especificamente com a herança. Com a sobrescrita, conseguimos especializar os métodos herdados das superclasses, alterando o seu comportamento nas subclasses por um mais específico.
A sobrescrita de métodos consiste basicamente em criar um novo método na classe filha contendo a mesma assinatura e mesmo tipo de retorno do método sobrescrito.
Quando mencionado anteriormente que o método deve possuir a mesma assinatura, significa dizer que o método deve possuir o mesmo nome, a mesma quantidade e o mesmo tipo de parâmetros utilizado no método sobrescrito.
Com relação ao tipo de retorno, este pode ser um subtipo do tipo de retorno do método sobrescrito, por exemplo: se o método da superclasse retornar um List, é permitido que o novo método retorne um ArrayList ou qualquer outro List. No entanto o oposto não é permitido, gerando um erro de compilação.
Mais detalhes sobre as regras de sobrescrita de métodos serão apresentados ainda neste artigo.
Para exemplificar e podermos aplicar esses conceitos, utilizaremos o seguinte cenário. Vamos imaginar a modelagem de um jacaré. O jacaré é um réptil que possui atividades em comum com outros animais, inclusive animais de outras espécies. Uma dessas atividades é a caça, por exemplo. No entanto, também possui atividades e características exclusivas dos répteis, assim como características próprias de um jacaré.
Com base nas descrições citadas acima, apresentaremos uma modelagem simples utilizando o diagrama de classes. Para isso, criaremos uma interface denominada Animal, e dentro dela definimos o método cacar(). A partir disso, garantimos que todo Animal implemente este método. Assim, ao codificarmos a classe Reptil, devemos implementar esta interface e deste modo definir o comportamento para o método cacar().
Com base nesta análise, chegamos ao diagrama apresentado na Figura 1. Os códigos da interface e da classe podem ser vistos nas Listagens 1 e 2, respectivamente.
Obs.: A modelagem seria mais bem elaborada se Reptil fosse uma classe abstrata, contendo alguns métodos a serem implementados por suas classes filhas, e outros já implementados que representariam comportamentos a serem herdados a todo Reptil. Porém, para o exemplo ficar mais simples, e para uso em algumas demonstrações que veremos em breve, manteremos Reptil como uma classe concreta.