Classes & métodos abstratos

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


Prof.: Danilo S. Carvalho

Nessa aula, vamos falar da combinação dos recursos de herança de classes e implementação de interfaces na forma de classes abstratas.


Ao final da aula, poderemos aplicar a abstração de classes e métodos ao nosso código.

Aprendemos até agora que podemos definir as características e comportamento dos objetos através de classes e suas capacidades desejadas através de interfaces.


Mas e se quisermos fazer ambos ao mesmo tempo? Ou seja, se quisermos definir atributos para os objetos, mas também métodos que gostariamos que tivessem, sem necessariamente implementá-los.

A maneira mais óbvia de fazer isso com o que sabemos até aqui, é colocando os atributos desejados em uma superclasse e fazendo ela implementar as interfaces desejadas.

Por exemplo, digamos que todas as pessoas no sistema da biblioteca serão autenticadas.

Implementado dessa forma, todas as subclasses de Pessoa e seus descendentes possuirão os atributos de Pessoa e o método autenticar() da interface Autenticavel.

Mas parando para pensar um pouco mais, notamos que qualquer classe que venhamos a utilizar no sistema da biblioteca vai representar um papel específico de uma pessoa nesse mundo. Logo nunca teremos um objeto apenas do tipo Pessoa.

Além disso, apenas pessoas são autenticáveis dentro da noção de funcionamento da biblioteca, mas diferentes modos de autenticação serão aplicados aos usuários e funcionários.

Precisamos então fazer com que a classe Pessoa funcione como um guia de implementação para seus descendentes, garantindo que o método de autenticação será sobrescrito, e que não existam instâncias de Pessoa sem um papel definido...

Mas também que seus atributos sejam herdados e utilizáveis por todos os seus descendentes.

                        
                            public class Pessoa implements Autenticavel {
                                private string cpf;
                                ...

                                public boolean autenticar(String cpf, String senha) {
                                    ...
                                }
                            }
                        
                    

A solução para esse problema é o uso de classes abstratas.

Uma classe abstrata possui os recursos de uma classe e uma interface ao mesmo tempo.

Para declarar uma classe abstrata, usamos a palavra chave abstract na declaração da classe.

Em uma classe abstrata, podemos declarar métodos sem implementação, ou seja, métodos abstratos.
Esses métodos precisarão ser implementados nas subclasses como nas interfaces.

Assim como as interfaces, uma classe abstrata não pode ser instanciada. Ela deve ser herdada por uma classe que, se for concreta (não abstrata), deve implementar todos os métodos abstratos que foram declarados.

Entretanto, a classe abstrata pode ter atributos de instância e de classe, e também métodos concretos, que funcionam como em uma classe concreta.

No exemplo abaixo, a classe Pessoa passou a ser abstrata, logo não podemos mais ter instâncias de Pessoa, apenas de seus descendentes concretos, que no momento são Usuario e Funcionario.

Usuario e Funcionario precisarão implementar cada um o seu próprio método autenticar.

Por polimorfismo, podemos garantir que toda subclasse de pessoa é autenticável.

                        
                            public abstract class Pessoa {
                                private string cpf;
                                ...

                                public boolean autenticar(String cpf, String senha);
                            }
                        
                    
                        
                            // Em algum outro lugar do programa...

                            public void autenticaPessoa(Pessoa) {
                                // Autentica uma pessoa independentemente do seu papel 
                                // e ajusta o estado apropriado do sistema.
                                ...
                            }
                        
                    

Usando classes abstratas e interfaces temos uma grande flexibilidade em como organizar nossos conceitos no código.


Nesse caso, as características e capacidades desejadas fazem parte de um mesmo conjunto, que determina o encapsulamento destas nas subclasses.


O polimorfismo garante a interoperabilidade dos objetos, tornando o encapsulamento prático e desejável.

Perguntas:

  1. Ao herdar uma classe abstrata, como faço para implementar apenas alguns dos métodos abstratos?
  2. A classe que inicia o programa pode ser abstract? Porquê?

Exercício:

  1. Com base nos exemplos até esse ponto, implemente um sistema simples para a biblioteca que permita a autenticação de usuários e funcionários. Os funcionários podem inserir novos livros e revistas e os usuários podem ler os livros e revistas. A interação com o sistema deve ser feita através de um menu textual, com opções numeradas para cada operação.

Até a próxima aula!