Herança

Computação II - Ciência da Computação


Prof.: Danilo S. Carvalho

Nessa aula, trataremos da possibilidade de reaproveitar a definição de classes para construir conceitos derivados ou generalizados daqueles que já temos.


Ao final da aula, poderemos implementar subclasses e entender o seu comportamento.

Durante a conceitualização dos objetos do mundo que queremos representar no computador em classes, muitas vezes nos deparamos com as seguintes situações:


1. Duas ou mais classes possuem características muito parecidas, ou apenas diferem em poucos detalhes.

2. Queremos criar subtipos de uma classe existente, mudando certas características ou comportamento.

No primeiro caso, desejamos alcançar um nível mais alto de abstração, generalizando conceitos diferentes em suas características comuns.


No segundo caso, procuramos especificar mais uma abstração existente para determinar detalhes úteis ao nosso programa.

Voltando ao nosso mundo da biblioteca, a administração decidiu incluir os funcionários da biblioteca no sistema.


Os funcionários também são pessoas, portanto poderiamos usar a classe Pessoas que já temos para representá-los, certo?


Mas precisamos de alguma maneira de distinguir entre os funcionários e os usuários da biblioteca, pois possuem certas características e papéis (e portanto ações) diferentes.

Resolvemos esses casos com o próximo conceito da Orientação a Objetos: a Herança.

A ideia é que uma classe pode "herdar" os atributos e métodos de outra classe, formando assim uma hierarquia.

Na figura abaixo, a abstração de um veículo é especificada progressivamente, adicionando características e ações (ex: forma de locomoção) diferentes.

No caso da nossa abstração de um carro, esta pode ser especificada em marcas, modelos, e assim por diante.

No caso da nossa abstração de um carro, esta pode ser especificada em marcas, modelos, e assim por diante.

No Java, a herança é implementada usando a palavra chave extends.

No exemplo abaixo, a classe Carro "estende", ou seja, herda os atributos (campos) e ações (métodos) da classe VeiculoComRodas.

Dizemos então que Carro é uma subclasse de VeiculoComRodas.

Todos os campos e métodos que forem declarados na classe Carro serão adicionados aos campos já existentes em VeiculoComRodas, sendo portanto um diferencial dessa classe com relação à sua classe mãe (parent class).

                        
                            public class Carro extends VeiculoComRodas {
                                ...
                            }
                        
                    

Vamos então aplicar isso ao nosso problema da biblioteca.

Sabemos que ambos os usuários e funcionários da biblioteca são pessoas, então trazemos para a classe pessoa toda a abstração comum entre essas duas categorias.

Como queremos que esses campos sejam acessíveis nas subclasses, trocamos o modificador de acesso deles para protected.

Resta agora a criação de cada uma das subclasses.

Dos usuários da biblioteca queremos manter os dados relativos ao uso dos livros: o número de páginas lidas o limite individual de atenção e a capacidade de leitura dos livros e revistas.

Dos funcionários queremos manter os dados relativos ao trabalho: matrícula de funcionário, horas trabalhadas, se estão em serviço ou não no momento e a capacidade de bater ponto.

                        
                            public class Pessoa {
                                private String cpf;
                                private String nome;
                                private LocalDateTime dataNascimento;
                                private String endereço;
                                private String telefone;
                            }
                        
                    
                        
                            public class Pessoa {
                                protected String cpf;
                                protected String nome;
                                protected LocalDateTime dataNascimento;
                                protected String endereço;
                                protected String telefone;
                            }
                        
                    
                        
                            public class Usuario extends Pessoa{
                                private int paginasLidas;
                                private int paginasAtento;

                                public void ler(Livro livro) {
                                    ...
                                }

                                public void ler(Revista revista) {
                                    ...
                                }
                            }
                        
                    
                        
                            public class Funcionario extends Pessoa{
                                private int matricula;
                                private float horasTrabalhadas;
                                private boolean emServico

                                public void baterPonto() {
                                    ...
                                }
                            }
                        
                    

Mas um funcionário não pode ser usuário da biblioteca?


Sim! Muitas vezes uma mesma entidade do mundo que observamos pode pertencer a mais de uma classe ao mesmo tempo.


Isso sempre vai ocorrer verticalmente (herança): todo funcionário é também uma pessoa, mas também pode ocorrer lateralmente (classes "irmãs").

No segundo caso, teremos dois objetos diferentes representando a mesma pessoa no mundo real, onde são mantidos separadamente o seu estado como funcionário e como usuário, por exemplo.


Como identificar que esses dois objetos diferentes representam a mesma pessoa é algo que trataremos em uma próxima aula.

E quanto aos construtores?

Os construtores de uma classe não são herdados pelas suas subclasses.

Entretanto, é possível chamar um contrutor da superclasse (a classe mãe) para inicializar os campos da subclasse, usando a palavra chave super.

Após chamar um construtor da superclasse, podemos continuar inicializando os campos da subclasse.

                        
                            public class Pessoa {
                                protected String cpf;
                                ...

                                public Pessoa(String cpf, String nome) {
                                    this.cpf = cpf;
                                    this.nome = nome;
                                }
                            }
                        
                    
                        
                            public class Usuario extends Pessoa{
                                private int paginasLidas;
                                private int paginasAtento;

                                public Usuario(String cpf, String nome) {
                                    super(cpf, nome);
                                }

                                ...
                            }
                        
                    
                        
                            public class Funcionario extends Pessoa{
                                private int matricula;
                                private float horasTrabalhadas;
                                private boolean emServico

                                public Funcionario(String cpf, String nome, int matricula) {
                                    super(cpf, nome);
                                    this.matricula = matricula;
                                }
                            }
                        
                    

Com isso, entendemos como podemos organizar as nossas classes em diferentes níveis de abstração.


O resultado dessa forma de pensar é o aumento do reuso do código e maior facilidade de compreensão.

Perguntas:

  1. Se um funcionário também é uma pessoa, então podemos atribuir uma referência a um objeto do tipo Funcionario a uma variável do tipo Pessoa?
  2. Como poderiamos mudar o estado armazenado em campos private de uma superclasse?
  3. Podemos herdar de mais de uma classe?

Exercício:

  1. Resolva a redundância das classes Livro e Revista no caso da biblioteca. Há rumores de que a administração da biblioteca vai trazer novos tipos de publicações: quadrinhos, jornais, e outros, e organizá-los em diferentes seções de acordo com o tipo. Seja um programador sagaz e prepare o código para as possíveis mudanças.

Até a próxima aula!