Você está na página 1de 11

Certificação Java

Programação Orientada a Objetos


por: Ulisses Telemaco
ulisses@jspbrasil.com.br

Esse módulo discute como alguns dos paradigmas de Programa ção Orientada a Objetos são implementados e
tratados em Java. Esse tema é importante para quem almeja uma certifica ção. Desde a versão 1.2, o exame
avalia, atravé s de códigos Java, os conhecimentos do programador em Orienta ção a Objeto. O teste concentra-
se basicamente em dois dos principais conceitos de Programa ção Orientada a Objetos: Tipos Abstratos e
Heran ça. Quando estudarmos heran ça veremos algumas regras de sobrecarga e sobrescrita de m étodos.

Tipos de Dados Abstratos

Um tipo abstrato de dados (TAD) é a junção de duas coisas: um modelo de dados e um conjunto de
comportamentos. Os comportamentos são representados por subprogramas (m é todos) e agem com
exclusividade sobre os dados encapsulados. Qualquer utilização dos dados encapsulados só poderá ser feita por
interm é dio dos mé todos definidos no TAD. Essa restrição é uma caracter ística OPCIONAL por ém ÚTIL em um
TAD.

Sobrecarga (Overloading) e Sobrescrita (overriding)

Sobrecarga e sobrescrita est ão relacionadas a reutiliza çao de um nome na construção de vários métodos.
Existem duas formas de fazer isso em Java: (a) reusando um nome com diferentes lista de par âmetros ou
usando o mesmo nome com a mesma lista de parâmetros. O primeiro caso é conhecido como sobrecarga
(overloading). Podem existir vários m étodos sobrecarregados numa mesma classe. O segundo caso é
conhecido como sobrescrita (overrinding ) e os m étodos devem aparecer em classes diferentes.

Sobrecarga de Métodos

Em Java, um m étodo é identificado pela seguinte combinação:


(a) qualificadores, (b) nome, (c) lista de argumentos;
Sobrecarga é o reuso de um mesmo nome (em uma classe ou subclasse) para diferentes m étodos.
Veja o exemplo:
1. public void abcMetodo(String s){ }
2. public void abcMetodo(int i, String s) { }
3. public void abcMetodo(String s, int i) { }
Figura 1: M étodos sobrecarregados

Os métodos ilustrados na Figura 1 tê m duas semelhanças: o nome e o valor de retorno. Porém suas listas de
parâmetros são diferentes. Somente o tipo dos argumentos são considerados, não seus nomes. Veja outro
exemplo:
1. public void abcMetodo(int j, String nome) {...}
2. public void abcMetodo(int i, String outronome) {...}
Figura 2: Métodos sobrecarregados 2

Os m étodos possuem o mesmo nome e a mesma lista de par âmetros e por isso o código acima é ilegal.

Chamando m étodos sobrecarregados


O compilador decide qual m étodo ser á chamado atrav és da lista de argumentos. Veja mais um exemplo:
1. public class ImpressaoLoop {
2. public static void imprimir(String s, int w){
3. for(int i=0; i<w; i++){
4. System.out.println(s);
5. }
6. }
7.
8. public static void imprimir(int i, int w){
9. imprimir("" + i, w);
10. }
11. }
Figura 3: Escolha de m étodos sobrecarregados

Existem dois m étodos sobrecarregados na classe acima: o primeiro deles é declarado na linha 2 e é identificado
pelo nome e pela lista de par âmetros (String e int). O segundo m étodo é declarado na linha 8 é é identificado
pelo nome e pela lista de par âmetros (int e int).

Perceba também que o segundo m é todo (linha 8) utiliza o segundo (linha 2). O mé todo que recebe o argumento
int converte o par âmetro para String e depois chama o mé todo da linha 2. Perceba que a lógica de impress ão é
implementada apenas no primeiro m étodo. É comum (não exigido) esse tipo de solução
.

Podemos sumarizar os pontos chaves da sobrecarga de m étodos da seguinte forma:

l Um m é todo é identificado pela seguinte combinação:

(a) qualificadores; (b) nome; (c) lista de par âmetros (tipo, ordem e quantidade);

l Dois ou mais métodos na mesma classe (incluindo métodos da subclasse) com os mesmos
nomes mas parâmetros diferentes são considerados "sobrecarregados" (overloaded ).
l Métodos com nomes sobrecarregados são independentes. Usar o mesmo nome é somente
uma conveni ência para o programador. Tipo de retorno, qualificadores, exceções e lista de
parâmetros são livres. Essa mesma liberdade não existe para a sobrescrita de m étodos. Veremos
a sobrescrita em detalhes em seguida...

Sobrescrita de Métodos

Quando uma classe é extendida, a subclasse herda todos os mé todos "não-privados" da classe pai. Algumas
vezes, é desej ável modificar o comportamento de um desses mé todos na nova classe. Para fazer isso basta
reescrever o método da classe pai utilizando o mesmo nome e a mesma lista de parâmetros (com algumas
restrições quanto ao tipo de retorno, uso de modificadores de acesso e utilização de exceções). Nesse caso
dizemos que o m étodo está sendo sobrescrito .
Figura 4: Escolha de m étodos sobrecarregados

Considere a Figura 4 que ilustra uma estrutura clássica. Os métodos não privados da classe Ponto estão
disponíveis para as classes Reta e Retangulo .
Assim, os métodos getDataCriacao e desenhar da classe Ponto est ão dispon íveis para as suas subclasses. O
primeiro desses mé todos recupera a data de cria ção do obteto. Já o segundo é responsável por desenhar o
objeto. Cada uma dessas classes tem um comportamento diferente para o método desenhar (são formas
diferentes de se desenhar um ponto, uma reta e um retângulo) e por isso o mé todo desenhar é sobrescrito em
cada classe.

Vejamos o código abaixo:


...
1. Ponto p[] = new Ponto[3];
2. p[0] = new Ponto();
3. p[1] = new Reta();
4. p[2] = new Retangulo();
5. for(int i=0; i<p.lenght; i++){
6. p[i].desenhar();
7. out.print("Data de criacao:"+p[i].getDataCriacao());
...
Figura 5: Chamada a mé todos sobrescritos

O array do código acima é criado como um conjunto de objetos do tipo Ponto . Contudo, o array é usado para
armazenar instâncias de Ponto , Reta e Ret ângulo . Nesse programa, o la ço executa os m étodos desenhar (linha
6) e getDataCriacao (linha 7) tr ê s vezes. Tais métodos devem estar associado com o objeto referenciado pelo
elemento do array. Assim, o programa acima chama o m é todo desenhar das classes Ponto , Reta e Retangulo,
respectivamente.
Já o m étodo getDataCriacao , chamado na linha 7, pertence é declarado apenas na classe Ponto .

Abaixo listamos alguns dos requisitos a serem respeitados na sobrescrita de m étodos:

a. O nome do m étodo, o tipo e a ordem dos argumentos deve ser a mesma do método original.
Se essa exig ência n ão for seguida ent ão n ão existe sobrescrita de m étodo e as regras abaixo são
ignoradas;
b. O tipo de retorno deve ser o mesmo;
c. O nível de acessibilidade não deve ser mais restrito que o mé todo original;
d. O novo m étodo não deve tratar exce ções que não são poss íveis para o m étodo original.

Os itens (c) e (d) serão vistos a seguir.

Sobrescrita e níveis de acessibilidade


O nível de acessibilidade de um m étodo sobrescrito não pode ser menor do que o m étodo original. Isto tem uma
raz ão bem simples. As subclasses são extensões de superclasses. Se algum dos métodos da classe original
tivesse seu nível de acessibilidade reduzido, então a subclasse não seria uma "extens ão" mas uma "redu ção".
Considere a estrutura da Figura 4 e o código da Figura 5. O que aconteceria se fosse possível sobrescrever o
mé todo desenhar reduzindo o seu nível de acessibilidade? Em tempo de compila ção, o método da linha 6 faz
referência a classe Ponto . Contudo, em tempo de execu ção, o sistema tentaria acessar o método desenhar da
classe Reta e Retangulo . A resposta a nossa pergunta é que se esse método fosse sobrescrito com nível de
acessibilidade menor que o original, então o sistema não resolveria a chamada aos métodos em tempo de
execu ção.
A Figura 6 ordena, por ordem de acessibilidade, os modificadores de acesso que podem ser usados na defini ção
de um m étodo:

Figura 6: Modificadores de acessibilidade

As regras de utiliza ção de modificadores de acessibilidade na sobrescrita de métodos podem ser resumidas a
seguir:

1. Um m é todo default pode ser sobrescrito para default , protected e public ;

2. Um m é todo protected pode ser sobrescrito para protected e public ;

3. Um m é todo public pode ser sobrescrito apenas por outro m étodo public .

É importante saber que, ao contr ário do que muitos livros dizem, um método private não pode ser
sobrescrito . A explicação é simples, os mé todos privados são exclusivos daquela classe. Como não são vistos
pelas subclasses então não podem ser sobrescritos. Vide se ção 'Sobrescrita x Método Convencional' deste
módulo para maiores detalhes.

Sobrescrita e tratamento de exceções

O item (d) determina que os mé todos sobrescritos não devem tratar exceções que não seriam tratadas pelos
mé todos originais. Existem uma raz ão bem simples para essa restri ção. Quando um m étodo sobrescreve outro,
ele deve manter algumas características do m étodo original para que o novo m étodo continue sendo usado nas
mesmas situações do mé todo original. Em outras palavras, o m étodo sobrescrito dever á ser usado em qualquer
lugar onde o m étodo original possa ser usado.
Vejamos o exemplo abaixo:
1. public class ClassePai{
2.
3. public void m1() {
4. ....
5. }
6.
7. }
Figura 7: Có digo da classe ClassePai

1. public class ClasseFilho extends ClassePai{


2.
3. public void m1() throws IOException {
4. ...
5. }
6.
7. public static void main(String a[]){
8.
9. ClassePai c = new ClasseFilho();
10. c.m1();
11.
12. }
13.
14.}
Figura 8: Código da classe ClasseFilho

Observe o m étodo main da Figura 8. É importante, nesse momento, saber que em tempo de compila ção, o compilador
faz referência ao método m1 da classe ClassePai . Em tempo de execução, a referência é feita ao m étodo m1 da classe
ClasseFilho .

O que se o mé todo m1 fosse sobrescrito com a adi ção de exceções não tratadas pelo m étodo original? Em tempo
de compila ção nenhum erro seria detectado porque o método original não trata nenhuma exce ção. Poré m, em
tempo de execução, a exce ção IOException poderia ser levantada e não existiria nenhum mecanismo para
tratá-la.

Vejamos mais um exemplo (essas classes foram listadas sequencialmente na Figura para facilitar a leitura mas,
na prática, são escritas em arquivos diferentes):
1. public class ClassePai {
2.
3. public void abc() throws IOException {
4. ...
5. }
6. }

7. public class ClasseFilhoOk_1 extends ClassePai {


8.
9. public void abc() throws IOException{
10. ...
11. }
12. }

13. public class ClasseFilhoOk_2 extends ClassePai {


14.
15. public void abc() {
16. ...
17. }
18. }

19. public class ClasseFilhoOk_3 extends ClassePai {


20.
21. public void abc() throws EOFException, MalFormedURLException {
22. ...
23. }
24. }

25. public class ClasseFilhoErro_1 extends ClassePai {


26.
27. public void abc() throws IOException, IllegalAccessException {
28. ...
29. }
30. }

31. public class ClasseFilhoErro_2 extends ClassePai {


32.
33. public void abc() throws Exception{
34. ...
35. }
36. }
Figura 9: Exemplos de sobrescritas de m étodos legais e ilegais

O método abc da classe ClassePai é declarado para tratar a exce ção IOException . Isto permite que esse
mé todo, ou qualquer m étodo que o sobrescreva, tratem a exce ção IOException ou subclasses dessa exceção.
Nesse caso, os métodos que sobrescrevem abc não podem tratar exceções que não sejam subclasses
de IOException .

Vejamos o que acontece com cada uma das subclasses declaradas na Figura 9:

l A classe ClasseFilhoOK_1 é legal porque o m étodo sobrescrito abc trata a mesma exceção do
mé todo original: IOException ;

l A classe ClasseFilhoOK_2 é legal porque o mé todo sobrescrito abc não trata nenhuma
exceção;

l A classe ClasseFilhoOK_3 é legal porque o método sobrescrito abc trata duas exce ções:
EOFException e MalFormedURLException . Ambas subclasses de IOException ;

l A classe ClasseFilhoErro_1 é ilegal porque uma das exceções tratadas pelo m étodo
sobrescrito abc não é subclasse de IOException ;

l A classe ClasseFilhoErro_2 é ilegal porque a exce ção tratada pelo método sobrescrito abc
não é subclasse de IOException . Nesse caso Exception é superclasse de IOException .

Chamando m étodos sobrescritos - palavra chave super

Considere o seguinte exemplo:

1. class Ponto {
2. int x, y;
3. public void desenhar(){
4. //DESENHA UM PONTO NA TELA
5. }
6. }
7.
8. class Reta extends Ponto {
9. Ponto p1, p2;
10. public void desenhar(){
11. //UTILIZA OS ATRIBUTOS E O METODO desenhar
12. //DA CLASSE PONTO PARA DESENHAR UMA RETA
13. for(PARA CADA PONTO ENTRE p1 E p2){
14. x = ...;
15. y = ...;
16. super.desenhar();
17. }
18. }
19. }
20.
21. class Retangulo extends Reta {
22. public void desenhar(){
23. //UTILIZA OS ATRIBUTOS E O METODO desenhar
24. //DA CLASSE RETA PARA DESENHAR UM RETANGULO
25. for(PARA CADA RETA DO RETANGULO){
26. p1.x = ...; p1.y = ...;
27. p2.x = ...; p2.y = ...;
28. super.desenhar();
29. }
30. }
31. }
Figura 10: Chamando m étodos da superclasse que foram sobrescritos

Na linha 10 o m é todo sobrescrito desenhar da classe Reta utiliza o m étodo original (da superclasse Ponto ) para
executar uma parte de seu trabalho. Uma chamada com a sintaxe "super.desenhar() " chama o mé todo da
superclasse que foi sobrescrito. Não importa se o m étodo é definido na superclasse imediata ou em alguma
classe ancestral: super chama a versã o do primeiro método encontrado na árvore de hierarquia das
classes.

Veja as Figura 11 e 12.

a b c
Figura 11: Busca de m étodos nas superclasses (sem a palavra chave 'super')

a b c
Figura 12: Busca de m étodos nas superclasses (com a palavra chave 'super')

No primeiro exemplo (Figura 11) a busca do método é feita na ordem mostrada: a busca pelo método é feita,
inicialmente, na própria classe. Depois a busca é feita nas superclasses.

No segundo exemplo, o uso da palavra chave super faz com que a busca inicie na superclasse imediata. N ão é
poss ível "pular" um nível na hierarquia. O compilador usa o primeiro método encontrado. Portanto, na Figura
12, a classe não pode chamar diretamente o m étodo a() da classe que está no topo da hierarquia.

Abaixo trazemos um resumo do que foi visto at é agora sobre sobrescrita de m étodos.

l Quando um m étodo é definido na subclasse com o mesmo nome e mesma lista de parâmetros
de um método da superclasse, dizemos que esse método sobrescreve (overriding) o m é todo da
superclasse;

l Cada mé todo da superclasse pode ser sobrescrito no máximo uma vez em uma mesma
subclasse. (N ão pode haver dois m étodos iguais na mesma classe);

l Um m é todo sobrescrito deve retornar exatamente o mesmo tipo do m étodo original;

l Um m é todo sobrescrito não deve ser menos acess ível que o m étodo original;

l Um método sobrescrito não deve tratar uma exceção que não foi declarada para o método
original. Assim, o método sobrescrito só poderá tratar as exceções tratadas pela classe original
ou subclasses delas;

l Um m étodo pode ser chamado de dentro de uma subclasse atrav é s da sintaxe super.metodo
() .

Sobrescrita x Método Convencional


Lembre -se que a sobrescrita de m é todos só ocorre para aqueles m é todos dispon íveis para as subclasses. Veja o
código da Figura 8:
1. public class ClassePai{
2.
3. public void m1(){
4. System.out.println("M étodo da classe Pai");
5. }
6.
7. public void m2(){
8. m1();
9. }
10.
11. public static void main(String a[]){
12.
13. ClassePai c = new ClasseFilho();
14. c.m2();
15. c.m1();
16.
17. ClasseFilho c2 = new ClasseFilho();
18. c2.m2();
19. c2.m1();
20.
21. }
22.
23. }
Figura 13: Vers ão 1 da Classe Pai: m étodo 'm1' público

1. public class ClasseFilho extends ClassePai{


2.
3. public void m1(){
4. System.out.println("M étodo da classe Filho");
5. }
6.
7. }
Figura 14: Vers ão 1 da Classe Filho: sobrescrita do mé todo 'm1'

As Figuras 13 e 14 trazem o código das classes ClassePai e ClasseFilho . Na subclasse o método m1


sobrescreve o m étodo da superclasse. A execu ção da ClassePai produz o seguinte resultado:

Figura 15: Resultado da execuçã o da 'ClassePai' (vers ão 1)

Sem muitas novidades para quem já chegou até aqui. Vejamos agora uma outra vers ão das classes ClassePai
e ClasseFilho :

1. public class ClassePai{


2.
3. private void m1(){
4. System.out.println("M étodo da classe Pai");
5. }
6.
7. public void m2(){
8. m1();
9. }
10.
11. public static void main(String a[]){
12.
13. ClassePai c = new ClasseFilho();
14. c.m2();
15. c.m1();
16.
17. ClasseFilho c2 = new ClasseFilho();
18. c2.m2();
19. c2.m1();
20.
21. }
22.
23. }
Figura 16: Vers ão 2 da Classe Pai: m étodo 'm1' privado

1. public class ClasseFilho extends ClassePai{


2.
3. public int m1(){
4. System.out.println("M étodo da classe Filho");
5. return 0;
6. }
7.
8. }
Figura 17: Vers ão 2 da Classe Filho: o m étodo 'm1' NÃO é sobrescrito

A primeira pergunta a fazer sobre o código acim á é: as classes compilam? É comum pensar que a classe
ClasseFilho n ão compila porque ela tenta sobrescrever o método m1 com um outro tipo de retorno. Contudo,
as duas classes compilam e o resultado da execução da ClassePai é apresentado na Figura abaixo:

Figura 18: Resultado da execuçã o da 'ClassePai' (vers ão 2)

A explica ção é simples: o m étodo m1 da superclasse é privado, não está disponível para as subclasses e por isso
não pode ser sobrescrito. O método m1 da classe ClasseFilho não sobrescreve o m étodo da superclasse, mas
sim, cria um outro m étodo de mesmo nome e mesma lista de parâmetros (NÃO É SOBRECARGA).
Na primeira versão das classes, o m é todo m2 da superclasse faz referência ao método m1 da classe
ClasseFilho .
Na segunda vers ão das classes, ocorre um fato interessante: como o mé todo m1 da superclasse não pode ser
sobrescrito (lembre-se que ele é privado da classe e que não está disponível para as subclasses) o compilador
resolve em tempo de compila ção a refer ê ncia que aparecesse no mé todo m2. Perceba que, ao contr ário da
versão anterior, não é feita refer ência ao mé todo m1 da classe ClasseFilho .

Construtores

Em geral os atributos e m étodos definidos em uma classe também estão dispon íveis nas suas subclasses. (vide
o módulo 3 - Modificadores para maiores informações). Os construtores, por sua vez, não são herdados. No
caso dos construtores é insuficiente defin í-los nas superclasses. Cada construtor, disponível para aquela classe,
deve ser definido explicitamente por ela. A exceção para isso são os construtores default que são fornecidos
automaticamente pelo compilador Java. Os cosntrutores default n a o tê m argumentos e são criados se, e
somente se, nenhum outro construtor for definido.

É comum fornecer construtores com argumentos e utilizá-los para controlar a constru ção de uma parte do
objeto "Pai". É poss ível chamar o construtor da superclasse atrav é s do comando super .

Considere o exemplo abaixo (essas classes foram listadas na mesma Figura para facilitar a leitura mas, na
prática, são escritas em arquivos diferentes):

1. public class Pessoa{


2. String nome;
3.
4. public Pessoa(String n){
5. nome = n;
6. }
7.
8.
9. }
10.
11. public class Aluno extends Pessoa {
12. int matricula;
13.
14. public Aluno(String n, int m){
15. super(n);
16. matricula = m;
17. }
18.
19. }
Figura 19: Utiliza ção da palavra 'super' para invocar construtores em superclasses

É importante saber que quando a palavra super for usada para chamar construtores nas superclasses, ela deve
ser o primeiro comando do construtor . Essa exig ência é feita para garantir que o construtor da superclasse
será chamado antes de executar qualquer configuração na subclasse.

Sobrecarga de Construtores

Embora os construtores não sejam herdados da mesma forma que os m étodos e os atributos, o mecanismo de
sobrecarga de construtores segue o mesmo princípio visto nas seções anteriores.

O código abaixo ilustra uma evolu ção da Figura 19:


1. class Pessoa{
2. String nome;
3.
4. public Pessoa(String n){
5. nome = n;
6. }
7.
8. public Pessoa(){
9. this("Anonimo");
10. }
11.
12. }
13.
14. class Aluno extends Pessoa {
15. int matricula;
16.
17. public Aluno(String n, int m){
18. super(n);
19. matricula = m;
20. }
21.
22. public Aluno(){
23. this("Anonimo",0);
24. }
25. }
Figura 20: Sobrecarga de construtores

Nesse exemplo os construtores das classes Pessoa e Aluno foram sobrecarregados. A escolha de qual construtor
será chamado é feita pela lista de parâmetros fornecida (semelhante a sobrecarga de m é todos).
Perceba que o segundo construtor não implementa, de fato, nenhuma funcionalidade. Ele aciona o primeiro
construtor fornecendo alguns argumentos. Essa t écnica é bastante comum e é implementada através do uso da
palavra chave this.
É importante saber que quando for utilizada, a palavra chave this deve ser o primeiro comando do construtor
(semelhante a palavra chave super ).

Não é permitido o uso das duas palavras chaves ( super() e this() ) no mesmo construtor. Então, o que fazer
quando existe a necessidade de chamar super() e this() para um mesmo construtor. Veja novamente o
código da Figura 20. A chamada a super() é feita somente nos construtores que implementam alguma
funcionalidade. Os outros construtores apenas os chamam (atrav és do comando this() ).

Caso o construtor não fa ça chamada aos comandos super() ou this() , então o compilador insere
automaticamente uma chamada ao construtor default da superclasse (construtor sem argumentos). Se uma
chamada explícita para outro construtor é feita atrav é s do comando this() , então o construtor da superclasse
não é chamado.

A razão para que os comandos super e this apare çam como o primeiro comando de um construtor é porque
um objeto deve ser iniciado "de cima para baixo".

Dissemos anteriormente que, se não existir o comando super ou this em um construtor, o compilador faz a
chamada autom ática ao construtor default da superclasse. Observe uma consequ ê ncia curiosa desse fato: se
você tentar extender uma classe que n ão fornece um construtor default (sem argumentos) então é necess ário
chamar um dos construtores da classe explicitamente ( super com os argumentos).

Abaixo segue um sum ário dos pontos vistos aqui sobre sobrecarga de construtores:

l Construtores não são herdados da mesma forma que atributos e m étodos;

l Se nenhum construtor for definido para uma classe, o compilador fornece um construtor
default sem argumentos. De outra forma, se pelo menos um construtor for definido, o compilador
não define um construtor default ;

l É comum fornecer vários construtores em uma mesma classe (construtores sobrecarregados).


Um construtor pode chamar outro através da palavra chave this. Se isto for feito, a palavra this
deve ser o primeiro comando no construtor;

l É poss ível fornecer uma chamada super (com ou sem argumentos) para controlar a inicia çao
da superclasse. Se isto for feito, ela deve ser o primeiro comando da implementa ção do
construtor.

Resumo de Sobrecarga e Sobrescrita

l Podem existir vários métodos sobrecarregados em uma mesma classe; Poré m um método
pode ser sobrescrito no máximo uma vez em uma subclasse. Na verdade essa restri ção decorre
do fato de não poder existir dois m étodo iguais (mesmo nome e mesma lista par âmetros) em
uma mesma classe;

l Métodos sobrecarregados devem ter argumentos diferentes. Métodos sobrescritos devem ter
argumentos id ê nticos. Na verdade se a lista de par âmetros N ÃO for id ê ntica a do m étodo original,
então não existe sobrescrita e sim sobrecarga;

l O tipo de retorno de um mé todo sobrecarregado pode ser escolhido livremente; o tipo de


retorno de um m étodo sobrescrito deve ser id êntico ao m étodo original.

l A sobrecarga de mé todos permite a implementação de várias funcionalidades utilizando o


mesmo nome. Sobrescrita, por outro lado, modifica a implementa ção de um método para uma
subclasse.

Você também pode gostar