Você está na página 1de 255

Programação orientada a objetos:

modelagem de problemas

APRESENTAÇÃO

O paradigma de programação orientada a objetos foi originalmente criado por Alan Kay, autor
da linguagem de programação Smalltalk. Embora seja um paradigma de programação mais novo
no que diz respeito ao seu emprego pelas linguagens de programação, algumas delas já haviam
implementado as suas ideias, sendo a Simula 67, criada em 1967, a primeira linguagem a
realmente utilizar a programação orientada a objetos.

A programação orientada a objetos trouxe uma nova maneira de se pensar projetos de softwares,
sendo que sua aplicação deixa bem clara as etapas de análise, projeto e codificação de softwares.
Seu conceito básico está em abstrair que tudo pode ser classificado como um objeto, cada objeto
implementando atributos e métodos e se relacionando a outros objetos, daí advém seu nome
“programa orientado aos objetos”. Conhecer e entender esse paradigma de programação é
fundamental, já que, atualmente, ele é implementado por muitas linguagens.

Nesta Unidade de Aprendizagem, você vai entender o que é programação orientada a objetos,
bem como vai conhecer algumas linguagens que o implementam e como são aplicados os seus
conceitos.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir programação orientada a objetos.


• Identificar as linguagens orientadas a objetos.
• Aplicar a programação orientada a objetos.

DESAFIO

O principal conceito da programação orientada a objetos é que todo programa é uma coleção de
objetos que se relacionam e apresentam comportamentos e atributos únicos.
1
Uma empresa contratou você para desenvolver um programa simples em uma linguagem
orientada a objetos. O escopo desse programa é controlar os pedidos feitos em uma lanchonete.
A seguir, no Desafio, veja como os pedidos são feitos atualmente e como eles deverão ser
realizados após o desenvolvimento do programa:

Com base nos requisitos que você viu, elabore uma descrição das possíveis classes, métodos e

2
atributos que seu programa deve apresentar para contemplar o escopo desse cenário. Agora, os
atributos e os métodos não precisam ser definidos, apenas identificados.

INFOGRÁFICO

A programação orientada a objetos contém alguns conceitos considerados pilares desse


paradigma. Para que uma linguagem de programação realmente seja considerada orientada a
objetos é necessário que ela implemente os pilares abstração, encapsulamento, herança e
polimorfismo.

A seguir, no Infográfico, veja o significado de cada um desses conceitos e o porquê de sua


relevância para a programação:

3
4
CONTEÚDO DO LIVRO

O paradigma de orientação a objetos é amplamente implementado pelas linguagens de


programação existentes. A programação orientada a objetos permite que o desenvolvimento de
um programa resulte de uma organização do contexto a que se propõe, implementando seu
universo como uma coleção de objetos que se relacionam e de que se abstraem características e
métodos únicos.

Leia o capítulo Programação orientada a objetos: modelagem de problemas, da obra


Paradigmas de programação, e veja como as linguagens de ampla adoção implementam esse
paradigma e, ainda, confira alguns exemplos de implementação de código em linguagem
orientada a objetos.

Boa leitura.

5
Programação orientada
a objetos: modelagem
de problemas
Objetivos de aprendizagem
Ao final deste texto, você deverá apresentar os seguintes aprendizados:

„„ Definir programação orientada a objetos.


„„ Identificar as linguagens orientadas a objetos.
„„ Aplicar a programação orientada a objetos.

Introdução
A programação orientada a objetos (POO) surgiu como um paradigma
que apresentava uma disciplina para novos projetos de desenvolvimento
de software. Etapas com análise e projeto de software que, em outros
paradigmas, acabavam sendo minimizados na POO, se tornam requisitos
fundamentais.
Não é difícil imaginar que a POO acabou se tornando um padrão con-
sumido pela engenharia de software e que possibilitou avanços significati-
vos na programação de computadores. Seu uso permitiu que reutilização
de código se tornasse algo praticamente comum em desenvolvimento
de software, servindo como base para novos conceitos, como os serviços.
Neste capítulo, você entenderá o conceito de POO, conhecerá al-
gumas linguagens que implementam esse paradigma e exemplos de
aplicação em projetos de programas de computador.

6
Programação orientada a objetos: modelagem de problemas

Programação orientada a objetos


O conceito de POO tem suas raízes na linguagem de programação Simula 67,
criada por Alan Kay, o precursor dos conceitos desse paradigma de programa-
ção. Contudo, sua grande evolução foi totalmente alcançada com a chegada
da linguagem de programação Smalltalk 80:

De fato, algumas pessoas consideram Smalltalk o modelo base para uma


linguagem de programação puramente orientada a objetos. Uma linguagem
orientada a objetos deve fornecer suporte para três recursos chave de lingua-
gem: tipos de dados abstratos, herança e vinculação dinâmica de chamadas
a métodos (SEBESTA, 2018, p. 547).

Linguagens que suportam a POO são, atualmente, muito usadas. Algumas


das linguagens de programação mais novas, projetadas para a POO, não
implementam mais conceitos de linguagens procedurais como as primei-
ras implementavam. Porém, ainda assim, empregam estruturas básicas da
programação estruturada e são imperativas, como as linguagens Java e C#,
atualmente muito utilizadas e consideradas exemplos de sucesso de POO.
Smalltalk 80 foi primeira linguagem a implementar completamente os
conceitos da POO, pois, em 1980, mesmo com a evolução dos módulos e pacotes
pelas linguagens de programação da época, os problemas ainda persistiam. Um
dos maiores problemas com os recursos atuais era que não existia mecanismo
para inicialização e finalização automática do tipo fornecido. Segundo Tucker
e Noonan (2009, p. 307) “As inicializações normalmente necessárias incluem
abertura de um arquivo, alocação de memória e inicialização de variáveis locais
ao módulo”. Já quanto às finalizações, incluem o fechamento de arquivos e
a liberação de memória.
Além disso, alguns programadores e projetistas começaram a perceber
que algumas necessidades não eram atendidas com as atuais linguagens de
programação imperativa de decomposição funcional e abstração de dados,
como os padrões de graphical user interfaces (GUI), que poderiam ser melhor
implementadas com o conceito de objetos que pudessem trocar mensagens
uns com os outros. Uma GUI poderia ser mais facilmente implementada se
considerarmos, por exemplo, que seus componentes (botões, áreas de texto,
imagens etc.) são tratados como objetos que podem interagir entre si e com
o usuário do sistema.

7
Programação orientada a objetos: modelagem de problemas

Assim, a POO surge como um paradigma centrado no desenvolvimento


de objetos, no lugar da atual decomposição funcional e abstração de dados.
Na Figura 1, você pode perceber um pouco dessa diferença entre a atual
programação estruturada e o conceito de objetos.

Programação estruturada POO


Método
Procedimento Dados objeto ...
Método
Procedimento
Método
Dados Procedimento Dados objeto ...
globais Procedimento
Método

...
...
Método
Procedimento
Dados objeto ...
Método

Figura 1. Comparativo entre programação estruturada e objetos.


Fonte: Adaptada de Gasparotto (2014).

Em uma linguagem de POO, o encapsulamento dos tipos de dados e suas


funções é alcançado por meio da implementação das classes. Uma classe é
uma declaração de um tipo de objeto que encapsula os seus tipos de dados
pelos seus atributos e funções por meio de seus métodos. É comum ouvir falar
que uma classe serve como uma matriz de objetos, pois, ao determinar os seus
atributos e métodos, serve como um modelo para que diversas instâncias de
um objeto sejam criadas a partir de sua estrutura.
Ao analisar a Figura 1, você pode perceber que um programa em progra-
mação estrutural possui um desempenho melhor que um mesmo programa
em POO, e isso ocorre pelo fato de, na programação estruturada, um código
ser executado após o outro sequencialmente, ao passo que na POO são ne-
cessários alguns desvios. Entretanto, a POO traz outros pontos que acabam
sendo mais interessantes no contexto de aplicações modernas. Como, na
maioria das aplicações, o desempenho das aplicações não é uma das grandes
preocupações (devido ao poder de processamento dos computadores atuais),
a POO se tornou muito difundida.

8
Programação orientada a objetos: modelagem de problemas

Na próxima seção, vamos abordar como as linguagens Java, C# e Python


implementam os conceitos da POO. Essas linguagens serão exemplos por serem
muito utilizadas atualmente no contexto de desenvolvimento de software.

Linguagens orientadas a objetos


Agora, você entenderá um pouco sobre as linguagens Java, C# e Python,
atualmente muito utilizadas e que implementam os conceitos da POO.
Java é uma linguagem de programação que foi desenvolvida pela Sun
Microsystems no início da década de 1990. Ela se tornou um símbolo da POO,
inclusive causando certa confusão, por julgarem que a POO era um paradigma
de Java e não ao contrário (MACHADO; FRANCO; BERTAGNOLLI, 2016,
p. 54).
De fato, a característica mais marcante da linguagem de programação
Java está relacionada a sua portabilidade, pois os sistemas construídos não
são compilados em código nativo da plataforma. Programas em Java são
compilados para um bytecode, que é executado por uma máquina virtual, a
Java virtual machine, que permite que os programas escritos em Java possam
ser rodados em qualquer plataforma compatível com a sua máquina virtual.
Em Java, todo o programa usa classes e objetos, e é fundamental que o
programador compreenda esses conceitos da POO para conseguir programar
em Java. Os programas são escritos em pequenos pedaços separados, chamados
de objetos. Segundo Machado, Franco e Bertagnolli (2016, p. 78), “Objetos
são pequenos programas que guardam dentro de si os dados — em suma, as
variáveis — que precisam para executar suas tarefas”. Os objetos também
trazem em si, como sub-rotinas, as instruções para processar esses dados.
As variáveis que um objeto guarda são chamadas de atributos, e as suas sub-
-rotinas são chamadas de métodos.

9
Programação orientada a objetos: modelagem de problemas

Veja o exemplo de uma classe Cachorro em Java com os atributos nome,


peso, altura e cor e o método pular().

public class Cachorro{


public String nome;
public float peso;
public float altura;
public String cor;

void pular{
if(altura>=80){
System.out.println("Seu Cachorro pula alto");

}
}
}

Como você pode observar, a programação em Java é praticamente regrada


pelos conceitos de POO, e as classes são a base de qualquer código Java.
Qualquer análise para um novo programa em Java deve partir do entendimento
do seu contexto e projeção das classes.
Agora, vamos analisar a linguagem C#. A linguagem C# (lê-se C Sharp)
é definida pela Microsoft, que a desenvolve como uma linguagem de POO
que faz parte de sua plataforma de desenvolvimento .NET. Embora a lingua-
gem C# tenha sido criada totalmente do zero pela Microsoft, foi baseada na
linguagem C++, e possui muitos elementos das linguagens Java e Pascal.
A plataforma .NET na qual teve seu desenvolvimento inicial, apresentou
algumas limitações que motivaram que, em 1999, fosse montada uma força
tarefa para o desenvolvimento de uma nova linguagem para essa plataforma.
Segundo Ledur (2018, p. 108):

A criação da linguagem C# ajudou muito no desenvolvimento do .NET, pois


a plataforma não precisou se adequar a nenhum código de alguma linguagem
já existente. O C# foi criado especificamente para .NET, sendo que muitas
outras linguagens têm suporte à C#.

10
Programação orientada a objetos: modelagem de problemas

Os principais objetivos do projeto da linguagem C# são:

„„ Ser simples moderna e de propósito geral OO.


„„ Ter uma linguagem e suas implementações que forneçam suporte para
princípios de engenharia de software.
„„ Ser destinada à utilização no desenvolvimento de componentes de
software.
„„ Possibilitar a portabilidade dos programas escritos em C#, assim como
é possível na linguagem Java.
„„ Fornecer suporte para a escrita de programa, tanto hospedados local-
mente como incorporados.

A Figura 2 mostra um exemplo da estrutura de uma classe em C#.

Figura 2. Exemplo da estrutura de uma classe em C#.


Fonte: Rodrigues (2017, documento on-line).

Você pode perceber que, assim como em Java, C# possui uma estrutura
semelhante com a declaração dos atributos da classe logo no início e depois em
seus métodos, além de uma semelhança na sintaxe entre as duas linguagens,
o que é explicado devido ao embasamento do C# na linguagem Java.

11
Programação orientada a objetos: modelagem de problemas

Para finalizar esta seção, vamos abordar a POO na linguagem de progra-


mação Python, que é uma linguagem de programação bastante utilizada por
sua facilidade de aprendizado, aliada as suas características de programação
de alto nível, de script, imperativa, OO e interpretada.
Python é uma linguagem que permite o desenvolvimento tanto no conceito
de programação estruturada como a POO. Possui suporte a tipificação dinâ-
mica, recursos de gerenciamento de uso de memória, além de oferecer uma
abrangente biblioteca padrão. Os interpretadores Python possuem suporte
para diversos sistemas operacionais, possibilitando a adaptação dos sistemas
construídos.
A origem do nome Python, apesar de confundido com o animal cobra,
na realidade é oriunda do grupo de comédia britânico que era assistido pelo
criador da linguagem, chamado Monty Python, formado por Graham Cha-
pman, John Cleese, Eric Idle, Michael Palin, Terry Jones e Terry Gilliam.
Carinhosamente, são chamados de Pythonistas os programadores Python,
e as referências aos trabalhos do grupo de comédia estão espalhadas pelos
tutoriais e sua documentação (LUTZ; ASCHER, 2007).
Python é uma linguagem muito simples, fácil de usar e aprender, apesar
disso, também é uma linguagem extremamente robusta e utilizada nas mais
diversas soluções:

„„ back-end de sistemas Web, customer relationship management (CRM),


ou gerenciamento de relacionamento com o cliente;
„„ pesadas simulações de engenharia;
„„ processamento pesado de efeitos especiais de filmes;
„„ soluções de análise de dados (data analytics);
„„ aprendizado de máquina, do inglês machine learning (ML).

Veja o exemplo de uma classe Pessoa em Python.

class Pessoa:
def _ init _ (self, nome, idade):
self.nome = nome
self.idade = idade

def setNome(self, nome):


self.nome = nome

12
Programação orientada a objetos: modelagem de problemas

def setIdade(self, idade):


self.idade = idade

def getNome(self):
return self.nome

def getIdade(self):
return self.idade

Apesar da sintaxe do Python ser um pouco diferente da sintaxe de Java e


C#, é possível verificar a estrutura da classe com a definição de atributos e
métodos e que Python é outro exemplar de linguagem OO. Na próxima seção,
você verá alguns exemplos da aplicação da programação OO em classes de
exemplos, para fixar o entendimento.

Desenvolvendo com programação


orientada a objetos
Para ajudar a elucidar o conceito de POO, vamos começar analisando a se-
guinte situação: um exemplo em Java que demonstra a criação da classe
Pessoa com os atributos nome, dataNascimento e CPF; e o método
tirarCopias, que calcula o custo da geração de cópias em um sistema de
gestão de impressões de uma escola. Esse sistema deve calcular o valor da
cópia embasado na seguinte regra:

„„ para alunos da escola, o valor unitário da cópia será de R$ 0,07;


„„ para os demais usuários, o valor unitário será de R$ 0,10.

A diferença é que este requisito será implementado com os seguintes


conceitos da POO:

„„ classes;
„„ heranças.

13
Programação orientada a objetos: modelagem de problemas

Observe na Figura 3, onde consta a classe Pessoa.

Figura 3. Classe Pessoa em Java.


Fonte: Geovane (2012, documento on-line).

Na classe Pessoa podemos observar os seguintes atributos:

„„ nome;
„„ cpf;
„„ data_nascimento.

Observamos, também, que essa classe possui o método tirarCopias,


que faz o cálculo do valor da cópia para pessoas em geral, ou seja, pessoas
que não são alunos. Porém, você pode estar se perguntando, onde estão os
dados do aluno e o método que faz o cálculo do valor da cópia quando se
trata de um aluno?
Esses dados estão na classe Aluno, mas, como o Aluno também é uma
pessoa e tem alguns atributos que são comuns as demais pessoas, não vamos
repetir na classe Aluno. A Figura 4 mostra como ficaria a classe Alunos
em Java.

14
Programação orientada a objetos: modelagem de problemas

Figura 4. Classe Alunos em Java.


Fonte: Geovane (2012, documento on-line).

É possível, portanto, observar que no início da classe Aluno existe a


declaração extends. Essa declaração significa que a classe Aluno está
dizendo que herda a classe Pessoa, dessa forma, os atributos que são comuns
à classe Pessoa não necessitam ser repetidos. Além disso, percebe-se que a
classe Aluno possui o atributo matrícula, que é específico do Aluno.
Por fim, veja que, na classe Aluno, o método tirarCopias é alterado
de acordo com o valor específico para os Alunos, o que é possível em razão
de um outro recurso de POO: o polimorfismo. Polimorfismo é um recurso que
permite ao objeto ter um comportamento diferente, dependendo da situação.
Nesse caso, o cálculo do valor da cópia se altera por conta da situação de
desconto de aluno.
Pelos exemplos apresentados, percebe-se na prática alguns recursos e usos
da programação OO e seus benefícios para a programação, possibilitando,
principalmente, a reutilização do código.
Conceitos como os observados nos exemplos das Figuras 3 e 4 são casos
comuns em POO, por isso é importante todo programador conseguir abstrair
classes com seus atributos e métodos separados e, quando utilizar conceitos
bases da POO, como herança de classes, evitar reutilização de código.

15
Programação orientada a objetos: modelagem de problemas

GASPAROTTO, H. M. Os 4 pilares da Programação Orientada a Objetos. DevMedia,


Rio de Janeiro, 2014. Disponível em: https://www.devmedia.com.br/os-4-pilares-da-
-programacao-orientada-a-objetos/9264. Acesso em: 15 set. 2019.
GEOVANE, H. Entendendo e Aplicando Herança em Java. DevMedia, Rio de Janeiro, 2012.
Disponível em: https://www.devmedia.com.br/entendendo-e-aplicando-heranca-em-
-java/24544. Acesso em: 15 set. 2019.
LEDUR, C. L. Desenvolvimento de sistemas com C#. Porto Alegre: SAGAH, 2018. 268 p.
LUTZ, M.; ASCHER, D. Aprendendo Python. 2. ed. Porto Alegre: Bookman; O’Reilly, 2007.
566 p.
MACHADO, R. P.; FRANCO, M. H. I.; BERTAGNOLLI, S. C. Desenvolvimento de software III:
programação de sistemas web orientada a objetos em Java. Porto Alegre: Bookman,
2016. 220 p. (Série Tekne; Eixo Informação e Comunicação).
RODRIGUES, J. Como criar minha primeira classe em C#. DevMedia, Rio de Janeiro, 2017.
Disponível em: https://www.devmedia.com.br/como-criar-minha-primeira-classe-em-
-csharp/38785. Acesso em: 15 set. 2019.
SEBESTA, R. W. Conceitos de linguagem de programação. 11. ed. Porto Alegre: Bookman,
2018. 758 p.
TUCKER, A. B.; NOONAN, R. E. Linguagens de programação: princípios e paradigmas. 2.
ed. Porto Alegre: AMGH, 2009. 630 p.

Leituras recomendadas
EDELWEISS, N.; LIVI, M. A. C. Algoritmos e programação: com exemplos em Pascal e C.
Porto Alegre: Bookman, 2014. 476 p. (Série Livros Didáticos Informática UFRGS).
MILETTO, E. M.; BERTAGNOLLI, S. C. Desenvolvimento de software II: introdução ao de-
senvolvimento web com HTML, CSS, JavaScript e PHP. Porto Alegre: Bookman, 2014.
276 p. (Série Tekne; Eixo Informação e Comunicação).
NICOLETTI, M. C. A cartilha Prolog. São Carlos: Edufscar, 2003. 124 p. (Série Apontamentos).
OKUYAMA, F. Y.; MILETTO, E. M.; NICOLAO, M. Desenvolvimento de software I: conceitos bá-
sicos. Porto Alegre: Bookman, 2014. 236 p. (Série Tekne; Eixo Informação e Comunicação).
PINHEIRO, F. A. C. Elementos de programação em C: em conformidade com o padrão
ISO / IEC 9899. Porto Alegre: Bookman, 2012. 548 p.

16
DICA DO PROFESSOR

Todo paradigma de programação tem vantagens e desvantagens, mas saber equilibrar os pontos
positivos e negativos, entendendo qual é o melhor para determinada situação, é o desafio de um
profissional de desenvolvimento de software.

A seguir, na Dica do Professor, veja algumas vantagens e desvantagens do paradigma de


programação orientado a objetos, o que possibilitará o entendimento de quando é necessária a
adoção de uma linguagem orientada a objetos.
Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) O conceito principal da programação orientada a objetos, que justifica o nome do


próprio paradigma, é que tudo pode ser abstraído para um objeto e, assim, um
programa seria uma coleção de objetos que se relacionam e apresentam
comportamento único. Assinale a alternativa que apresenta os principais
componentes de um objeto:

A) Estados e atributos.

B) Métodos e colunas.

C) Tabelas e variáveis.

D) Atributos e métodos.

E) Estado e métodos.

2) A orientação a objetos trouxe alguns conceitos interessantes para a programação, e

17
um deles está relacionado ao conhecimento sobre a implementação interna da classe,
o qual é desnecessário do ponto de vista do objeto. Assinale a alternativa correta
quanto ao seu nome:

A) Relacionamentos.

B) Encapsulamento.

C) Classes.

D) Abstração.

E) Herança.

3) Um dos novos conceitos surgidos sobre a programação orientada a objeto é o de que


ela é um mecanismo por meio do qual é possível selecionar as funcionalidades
utilizadas de forma dinâmica por um programa no decorrer de sua execução.
Assinale a alternativa correta que apresenta o nome desse mecanismo:

A) Atributos.

B) Herança.

C) Classes.

D) Abstração.

E) Polimorfismo.

4) Uma das grandes vantagens da adoção da programação orientada a objetos é a

18
reutilização de código e sua organização. Com relação às vantagens da reutilização de
código na programação orientada a objetos, assinale a alternativa correta:

A) A reutilização de código é obtida pelo uso de funções nos programas.

B) Os códigos em linguagens OO são simplificados pelo uso de procedimentos.

C) A reutilização de código é consequência da análise e do projeto de código.

D) A reutilização de código é consequência de métodos e eventos.

E) A reutilização de código se obtém após a sua simplificação.

5) Um objeto é uma instância de uma classe em programação orientada a objetos. As


classes definem, então, a estrutura dos objetos que serão instanciados a partir dela.
Com base nessa afirmativa, analise a classe a seguir em Java e assinale a alternativa
correta:

public class Bola


{
String cor;
int tamanho;
boolean cheia;
void encher()
{
cheia = true;
}
void esvaziar()
{
cheia = false;
}
}

19
A) Cor, tamanho e cheia são métodos da classe bola.

B) Esvaziar e encher são atributos do tipo void, da classe bola.

C) Cor, tamanho e cheia são atributos da classe bola.

D) Esvaziar, encher e cheia são métodos da classe bola.

E) Cor e tamanho são atributos e cheia método booleano da classe bola.

NA PRÁTICA

Python é uma linguagem de programação orientada a objetos com grande aceitação no


mercado. Atualmente, ela está entre uma das linguagens de programação mais utilizadas,
principalmente por ser uma linguagem de fácil aprendizado que possibilita a profissionais
conhecedores de qualquer outra linguagem orientada a objetos implementar programas
rapidamente. Além disso, ela pode se adaptar a diferentes contextos de negócio.

A seguir, no Na Prática, veja o desenvolvimento de uma estrutura simples com operações


básicas para serem utilizadas no gerenciamento de uma conta de um banco e confira como a
linguagem Python implementa a programação orientada a objetos:

20
21
SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Entendendo a orientação a objetos

A programação orientada a objetos é um paradigma de programação, isto é, uma forma de


programar e desenvolver um sistema baseada no conceito de objetos, que podem conter dados
na forma de campos, os quais também são denominados atributos, e códigos, na forma de
procedimentos conhecidos como métodos. No vídeo a seguir, veja o conceito de orientação a
objetos, classes, atributos e métodos.

Conteúdo interativo disponível na plataforma de ensino!

Aplicando herança em Java

Quando se faz uma programação em Java, existe a necessidade de se trabalhar com várias
classes. Isso se explica pelo fato de que, na maioria das vezes, classes diferentes apresentam
características comuns e, assim, ao invés de criar outra classe com essas características, é
possível usar as características de um objeto ou classe já existente. Essa é a herança. Confira, no
vídeo a seguir, a aplicação do conceito de herança em JAVA.

Conteúdo interativo disponível na plataforma de ensino!

22
Introdução a classes, objetos e métodos

APRESENTAÇÃO

Nesta Unidade de Aprendizagem estudaremos classes, objetos e métodos, conceitos de extrema


importância para a evolução em programação orientada a objetos. As classes abrigam
elementos, como atributos e métodos, definindo tanto os dados quanto os códigos que operam
sobre os mesmos, ou seja, classes são como um plano, que determinam como o objeto será
criado.

Os métodos são pedaços de código ou sub-rotinas, que tratam os dados definidos por uma
classe. Objetos são instâncias de uma classe, são compostos por estado e comportamento e se
comunicam através de mensagens.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir classes, objetos e métodos.


• Construir uma classe com seus elementos, com auxílio de uma linguagem de programação
orientada a objetos.
• Criar objetos.

DESAFIO

Para desenvolvermos uma aplicação, é necessário fazermos um levantamento de requisitos, ou


seja, escrever quais as necessidades e objetivos que ela deverá atingir. Com isso, estamos
construindo o domínio da aplicação, que define para que estamos criando esta solução.

Você trabalha no setor de informática em uma escola que ainda não possui uma gestão
informatizada, e foi encarregado de criar o cadastro de alunos.

23
O resultado a ser entregue será um arquivo zipado do projeto, o qual poderá ser construído em
qualquer IDE (NetBeans, eclipse, etc.).

INFOGRÁFICO

Observe no infográfico uma descrição de classe, objeto e método.

24
CONTEÚDO DO LIVRO

O paradigma de programação orientada a objetos é fundamentado na construção de classes, as


quais possuem elementos que definem e manipulam dados. Portanto, para a construção de um
sistema orientado a objetos, é necessário o conhecimento nestes fundamentos.

Acompanhe um trecho da obra Java para iniciantes, livro que serve de base teórica para esta
Unidade de Aprendizagem e traz uma abordagem sobre classes, atributos e métodos. O domínio
destes conceitos é vital para o desenvolvimento de softwares. Inicie o estudo pelo tópico
Fundamentos das classes e finalize em Como os objetos são criados.

Boa leitura.

25
Atualizado para JAVA SE 8 (JDK 8)

Java
para iniciantes

Crie, compile e execute programas Java rapidamente

Herbert Schildt
26
O autor
O autor de best-sellers Herbert Schildt escreve incansavelmente sobre programação
há quase três décadas e é uma das principais autoridades na linguagem Java. Seus
livros de programação venderam milhões de cópias no mundo inteiro e foram tra-
duzidos para diversos idiomas. É autor de vários livros sobre Java, incluindo Java:
The Complete Reference, Herb Schildt’s Java Programming Cookbook e Swing: A
Beginner’s Guide. Ele também escreveu sobre C, C++ e C#. Embora tenha interesse
em todas as áreas da computação, seu foco principal são as linguagens de progra-
mação, incluindo compiladores, interpretadores e linguagens de controle robótico.
Também tem grande interesse na padronização de linguagens. Schildt tem gradua-
ção e pós-graduação pela Universidade de Illinois. Seu site é www.HerbSchildt.com.

O editor técnico
Dr. Danny Coward trabalhou em todas as edições da plataforma Java. Ele conduziu
a definição dos Java Servlets para a primeira versão da plataforma Java EE e para
além dela, os serviços web para a plataforma Java ME, e a estratégia e planejamento
de Java SE 7. Fundou a tecnologia JavaFX e, mais recentemente, projetou o maior
acréscimo feito ao padrão Java EE 7, a API Java WebSocket. Da codificação em Java
ao projeto de APIs com especialistas da indústria e ao trabalho por vários anos como
executivo do Java Community Process, ele adquiriu uma perspectiva singularmente
ampla de vários aspectos da tecnologia Java. Além disso, é autor de JavaWebSo-
cket Programming e de um livro ainda a ser publicado sobre Java EE. Dr. Coward
tem graduação, mestrado e doutorado em Matemática pela Universidade de Oxford.

S334j Schildt, Herbert.


Java para iniciantes : crie, compile e execute programas
Java rapidamente [recurso eletrônico] / Herbert Schildt ;
tradução: Aldir José Coelho Corrêa da Silva ; revisão
técnica: Maria Lúcia Blanck Lisbôa. – 6. ed. – Porto Alegre :
Bookman, 2015.

Editado como livro impresso em 2015.


ISBN 978-85-8260-337-6

1. Linguagem de programação - Java. I. Título.

CDU 004.438Java

Catalogação na publicação: Poliana Sanchez de Araujo – CRB 10/2094

27
Capítulo 4 Introdução a classes, objetos e métodos

Principais habilidades e conceitos


• Saber os fundamentos da classe
• Entender como os objetos são criados
• Entender como as variáveis de referência são atribuídas
• Criar métodos, retornar valores e usar parâmetros
• Usar a palavra-chave return
• Retornar um valor de um método
• Adicionar parâmetros a um método
• Utilizar construtores
• Criar construtores parametrizados
• Entender new
• Entender a coleta de lixo e os finalizadores
• Usar a palavra-chave this

.....................................................................................................................................

A ntes de poder se adiantar mais em seu estudo de Java, você precisa conhecer a
classe. Classe é a essência de Java. Ela é a fundação na qual toda a linguagem
Java se estrutura, porque define a natureza de um objeto. Como tal, ela forma a base
da programação orientada a objetos em Java. Dentro de uma classe, são definidos
dados e o código que age sobre eles. O código fica contido em métodos. Já que as
classes, objetos e métodos são fundamentais para Java, eles serão introduzidos neste
capítulo. Ter um entendimento básico desses recursos permitirá que você escreva
programas mais sofisticados e compreenda melhor certos elementos-chave de Java
descritos no próximo capítulo.

Fundamentos das classes


Já que toda a atividade dos programas Java ocorre dentro de uma classe, temos usado
classes desde o início deste livro. É claro que só classes extremamente simples foram
usadas e não nos beneficiamos da maioria de seus recursos. Como você verá, elas
são significativamente mais poderosas do que as classes limitadas apresentadas até
agora.
Comecemos examinando o básico. Uma classe é um modelo que define a for-
ma de um objeto. Ela especifica tanto os dados quanto o código que operará sobre
eles. Java usa uma especificação de classe para construir objetos. Os objetos são
instâncias de uma classe. Logo, uma classe é basicamente um conjunto de planos

28
Java para Iniciantes

que especifica como construir um objeto. É importante deixar uma coisa bem clara:
uma classe é uma abstração lógica. Só quando um objeto dessa classe é criado é que
existe uma representação física dela na memória.
Outro ponto: lembre-se de que os métodos e variáveis que compõem uma clas-
se são chamados de membros da classe. Os membros de dados também são chama-
dos de variáveis de instância.

Forma geral de uma classe


Quando definimos uma classe, declaramos sua forma e natureza exatas. Fazemos
isso especificando as variáveis de instância que ela contém e os métodos que operam
sobre elas. Embora classes muito simples possam conter apenas métodos ou apenas
variáveis de instância, a maioria das classes do mundo real contém ambos.
Uma classe é criada com o uso da palavra-chave class. Uma forma geral sim-
plificada de uma definição class é mostrada aqui:
class nome da classe {
// declara variáveis de instância
tipo var1;
tipo var2;
// ...
tipo varN;

// declara métodos
tipo método1(parâmetros) {
// corpo do método
}
tipo método2(parâmetros) {
// corpo do método
}
// ...
tipo métodoN(parâmetros) {
// corpo do método
}
}
Embora não haja essa regra sintática, uma classe bem projetada deve definir
apenas uma entidade lógica. Por exemplo, normalmente, uma classe que armazena
nomes e números de telefone não armazena também informações sobre o mercado
de ações, a média pluviométrica, os ciclos das manchas solares ou outros dados não
relacionados. Ou seja, uma classe bem projetada deve agrupar informações logi-
camente conectadas. A inserção de informações não relacionadas na mesma classe
desestruturará rapidamente seu código!
Até o momento, as classes que usamos tinham apenas um método: main( ).
Você verá como criar outros em breve. No entanto, observe que a forma geral de uma
classe não especifica um método main( ). O método main( ) só é necessário quando
a classe é o ponto de partida do programa. Alguns tipos de aplicativos Java, como os
applets, também precisam de um método main( ).

29
Capítulo 4 Introdução a classes, objetos e métodos

Definindo uma classe


Para ilustrar as classes, desenvolveremos uma classe que encapsula informações so-
bre veículos, como carros, furgões e caminhões. Essa classe se chama Vehicle e con-
terá três informações sobre um veículo: o número de passageiros que ele pode levar,
a capacidade de armazenamento de combustível e o consumo médio de combustível
(em milhas por galão).
A primeira versão de Vehicle é mostrada a seguir. Ela define três variáveis de
instância: passengers, fuelcap e mpg. Observe que Vehicle não contém método.
Logo, atualmente é uma classe só de dados. (Seções subsequentes adicionarão mé-
todos a ela.)
class Vehicle {
int passengers; // número de passageiros
int fuelcap; // capacidade de armazenamento de combustível em galões
int mpg; // consumo de combustível em milhas por galão
}

Uma definição class cria um novo tipo de dado. Nesse caso, ele se chama Vehi-
cle. Você usará esse nome para declarar objetos de tipo Vehicle. Lembre-se de que
uma declaração class é só uma descrição de tipo; ela não cria um objeto real. Logo, o
código anterior não faz nenhum objeto de tipo Vehicle passar a existir.
Para criar realmente um objeto Vehicle, você usará uma instrução como a mos-
trada abaixo:
Vehicle minivan = new Vehicle(); // cria um objeto Vehicle chamado minivan

Após essa instrução ser executada, minivan será uma instância de Vehicle. Portanto,
terá realidade “física”. Por enquanto, não se preocupe com os detalhes da instrução.
Sempre que você criar uma instância de uma classe, estará criando um objeto
contendo sua própria cópia de cada variável de instância definida pela classe. Logo,
todos os objetos Vehicle conterão suas próprias cópias das variáveis de instância
passengers, fuelcap e mpg. Para acessar essas variáveis, você usará o operador pon-
to (.). O operador ponto vincula o nome de um objeto ao nome de um membro. A
forma geral do operador ponto é mostrada aqui:
objeto.membro
Portanto, o objeto é especificado à esquerda e o membro é inserido à direita. Por
exemplo, para atribuir o valor 16 à variável fuelcap de minivan, use a instrução a
seguir:
minivan.fuelcap = 16;

Em geral, podemos usar o operador ponto para acessar tanto variáveis de instância
quanto métodos.
Este é um programa completo que usa a classe Vehicle:
/* Um programa que usa a classe Vehicle.

Chame este arquivo de VehicleDemo.java


*/
class Vehicle {

30
Java para Iniciantes

int passengers; // número de passageiros


int fuelcap; // capacidade de armazenamento de combustível em galões
int mpg; // consumo de combustível em milhas por galão
}

// Essa classe declara um objeto de tipo Vehicle.


class VehicleDemo {
public static void main(String args[]) {
Vehicle minivan = new Vehicle();
int range;

// atribui valores a campos de minivan


minivan.passengers = 7;
minivan.fuelcap = 16; Observe o uso do operador ponto
minivan.mpg = 21; para o acesso a um membro.

// calcula a autonomia presumindo um tanque cheio de gasolina


range = minivan.fuelcap * minivan.mpg;
System.out.println("Minivan can carry " + minivan.passengers +
" with a range of " + range);
}
}

Você deve chamar o arquivo que contém o programa de VehicleDemo.java,


porque o método main( ) está na classe chamada VehicleDemo e não na classe cha-
mada Vehicle. Quando compilar esse programa, verá que dois arquivos .class foram
criados, um para Vehicle e um para VehicleDemo. O compilador Java insere auto-
maticamente cada classe em seu próprio arquivo .class. Não é necessário as classes
Vehicle e VehicleDemo estarem no mesmo arquivo-fonte. Você pode inserir cada
classe em seu próprio arquivo, chamados Vehicle.java e VehicleDemo.java, respec-
tivamente.
Para executar o programa, você deve executar VehicleDemo.java. A saída a
seguir é exibida:
Minivan can carry 7 with a range of 336

Antes de avançar, examinemos um princípio básico: cada objeto tem suas pró-
prias cópias das variáveis de instância definidas por sua classe. Logo, o conteúdo das
variáveis de um objeto pode diferir do conteúdo das variáveis de outro. Não há co-
nexão entre os dois objetos exceto pelo fato de serem do mesmo tipo. Por exemplo,
se você tiver dois objetos Vehicle, cada um terá sua própria cópia de passengers,
fuelcap e mpg, e o conteúdo dessas variáveis será diferente entre os dois objetos. O
programa abaixo demonstra esse fato. (Observe que a classe que tem main( ) agora
se chama TwoVehicles.)
// Este programa cria dois objetos Vehicle.

class Vehicle {
int passengers; // número de passageiros
int fuelcap; // capacidade de armazenamento de combustível em galões

31
Capítulo 4 Introdução a classes, objetos e métodos

int mpg; // consumo de combustível em milhas por galão


}

// Essa classe declara um objeto de tipo Vehicle.


class TwoVehicles {
public static void main(String args[]) {
Vehicle minivan = new Vehicle(); Lembre-se de que minivan
e sportscar referenciam
Vehicle sportscar = new Vehicle();
objetos separados.

int range1, range2;

// atribui valores a campos de minivan


minivan.passengers = 7;
minivan.fuelcap = 16;
minivan.mpg = 21;

// atribui valores a campos de sportscar


sportscar.passengers = 2;
sportscar.fuelcap = 14;
sportscar.mpg = 12;

// calcula a autonomia presumindo um tanque cheio de gasolina


range1 = minivan.fuelcap * minivan.mpg;
range2 = sportscar.fuelcap * sportscar.mpg;

System.out.println("Minivan can carry " + minivan.passengers +


" with a range of " + range1);

System.out.println("Sportscar can carry " + sportscar.passengers +


" with a range of " + range2);
}
}

A saída produzida por esse programa é mostrada aqui:


Minivan can carry 7 with a range of 336
Sportscar can carry 2 with a range of 168

Como você pode ver, os dados de minivan são totalmente diferentes dos contidos em
sportscar. A ilustração a seguir mostra essa situação.

passengers 7
minivan
fuelcap 16
mpg 21

passengers 2
sportscar
fuelcap 14
mpg 12

32
Java para Iniciantes

Como os objetos são criados


Nos programas anteriores, a linha abaixo foi usada para declarar um objeto de tipo
Vehicle:
Vehicle minivan = new Vehicle();

Essa declaração faz duas coisas. Em primeiro lugar, ela declara uma variável chamada
minivan da classe Vehicle. Essa variável não define um objeto. Em vez disso, ela pode
apenas referenciar um objeto. Em segundo lugar, a declaração cria uma cópia física do
objeto e atribui à minivan uma referência a ele. Isso é feito com o uso do operador new.
O operador new aloca dinamicamente (isto é, aloca no tempo de execução)
memória para um objeto e retorna uma referência a ele. Essa referência é, mais ou
menos, o endereço do objeto na memória alocado por new. A referência é então
armazenada em uma variável. Logo, em Java, todos os objetos de uma classe devem
ser alocados dinamicamente.
As duas etapas da instrução anterior podem ser reescritas desta forma para
mostrarmos cada etapa individualmente:
Vehicle minivan; // declara uma referência ao objeto
minivan = new Vehicle(); // aloca um objeto Vehicle

A primeira linha declara minivan como referência a um objeto de tipo Vehicle. Por-
tanto, minivan é uma variável que pode referenciar um objeto, mas não é um objeto.
Por enquanto, minivan não referencia um objeto. A próxima linha cria um novo ob-
jeto Vehicle e atribui à minivan uma referência a ele. Agora, minivan está vinculada
a um objeto.

33
DICA DO PROFESSOR

Projetar um software é como projetar uma casa. Tanto a engenharia civil quanto a de software
possuem um esquema ou desenho.

Na engenharia civil, chamamos de desenho arquitetônico ou planta; na engenharia de software,


chamamos de arquitetura ou designer, todos inclusos na fase de projeto. Com isso, as pessoas
que farão devem saber interpretar tais desenhos.

Agora, faremos uma aplicação prática, em que interpretaremos o desenho de uma classe e
implementaremos seu código.
Os objetivos são :

- Implementar uma classe chamada Media, definindo atributos e métodos para calcular a média
aritmética entre duas notas.

- Implementar uma classe chamada Principal, para instanciar a classe Media.

- Utilizar uma caixa de mensagens para mostrar o resultado na tela; para isso, importaremos a
classe JOptionPane do pacote swing do java.

A aplicação consiste em receber duas notas e calcular a média aritmética. Para o desenho, foi
utilizada a ferramenta Astah Community que utiliza as notações UML (linguagem unificada de
modelagem).

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Em programação orientada a objetos, qual o papel da classe?

34
A) Classes possuem um papel irrelevante na programação orientada a objetos.

B) As classes são a fundação, na qual linguagens orientadas a objetos se estruturam.

C) Classes são boas práticas de programação.

D) Dentro de uma classe são escritos todos comentários de uma aplicação.

E) Uma classe é, basicamente, um conjunto de planos que especifica como construir um


pacote.

2) Em relação à forma geral de uma classe, podemos afirmar:

A) Uma classe é criada com o uso da palavra-chave public.

B) É impossível usar comentários dentro de uma classe.

C) Uma classe é criada com a palavra-chave class.

D) Os parênteses são caracteres que delimitam o bloco da classe.

E) Podemos usar palavras reservadas de uma linguagem de programação para nomearmos


classes.

3) Sobre objetos, podemos afirmar que:

A) É uma abstração lógica.

B) Um objeto é composto por estado e comportamento.

35
C) Para criarmos uma classe, é necessário seguirmos a especificação de um objeto.

D) Considere o seguinte código: “Cliente cli = new Cliente();” a palavra “new” é o método
construtor da classe.

E) Não há comunicação entre objetos dentro de um sistema orientado a objetos.

4) Analise o seguinte código:

public class Veiculo {


private String placa;
private String cor;

private int anoFabricacao;

public String getPlaca() {

return placa;

public void setPlaca(String placa) {


this.placa = placa;

public String getCor() {

return cor;

public void setCor(String cor) {

this.cor = cor;
36
}

public int getAnoFabricacao() {

return anoFabricacao;

public void setAnoFabricacao(int anoFabricacao) {

this.anoFabricacao = anoFabricacao;

A) A classe Veiculo tem quatro variáveis de instância.

B) As palavras-chave this em this.cor, this.placa e this.anoFrabricacao informam ao método


que estamos nos referindo à variável de instância.

C) A palavra void é necessária para criação de todos os métodos.

D) As variáveis de instância da classe veículo poderão ser visualizadas por qualquer outra
classe da aplicação.

E) O código está incorreto, pois métodos e variáveis de instância não são elementos de uma
classe.

5) São implementados para realizarem algum tipo de tarefa:

A) Classes.
37
B) Objetos.

C) Atributos.

D) Métodos.

E) Comentários.

NA PRÁTICA

O grande objetivo do paradigma de programação orientada a objetos é facilitar a abstração do


mundo real e transformá-lo em código. O mundo é composto por coisas ou objetos. Quem nunca
passou pela situação de pedir que alcance aquele objeto? Com isso, podemos exemplificar como
sendo objetos, pessoas, carros, animais, entre outros.

Todo objeto é composto por características e seus valores. A essas características e valores, dá-
se o nome de estado do objeto, é o que o unifica em relação a outros objetos, por exemplo: um
objeto pessoa pode ter como característica o seu nome, e esse nome pode ter o valor "João",
"Paulo", etc. Todo objeto possui um comportamento, isto é, uma ação; a pessoa, pode falar,
comer; o carro pode acelerar, frear... Esses comportamentos do objeto são chamados de métodos
.

38
As classes têm uma característica importante, pois, além de classificar objetos com atributos
semelhantes, elas modelam a criação destes objetos.

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Curso POO Java #02b - Criando Classes e Objetos em Java

Aprenda como criar uma Classe, com seus atributos e métodos, e a instanciar novos objetos a
partir dessa classe inicial

Conteúdo interativo disponível na plataforma de ensino!

Orientação a Objetos em Java

Essa sessão visa ensinar os conceitos do paradigma de Orientação a Objetos (OO).

Conteúdo interativo disponível na plataforma de ensino!

Conceitos de Computação com Java - Compatível com Java 5 & 6

Cay Horstmann, 5ª edição, 2009

39
Projetando classes

APRESENTAÇÃO

Para desenvolvermos aplicações utilizando o paradigma orientação a objetos, podemos definir


que projetar classes é um importante desafio. Segundo Cay Horstmann (2009), classes são
coleções de objetos, portanto, precisamos começar a atividade de programação identificando os
objetos e as classes às quais eles pertencem.

Nesta Unidade de Aprendizagem você verá os conceitos de coesão e acoplamento, que são
importantes para formar uma estrutura de classes consistente e, por fim, pacotes, que são uma
forma de organizar as classes do nosso projeto.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Identificar objetos e definir a quais classes eles pertencem.


• Definir coesão e acoplamento.
• Usar pacotes para organizar classes.

DESAFIO

Projetar uma classe pode ser um bom desafio, mas quando seguimos algumas regras e padrões,
nosso trabalho se torna menos árduo.

Você trabalha como analista/programador em uma fábrica de softwares e foi encarregado de


atender um cliente que possui uma oficina mecânica. O cliente necessita automatizar o processo
de manutenção de veículos, ou seja, fazer um controle dos clientes e dos veículos que os
mesmos trazem para manutenção. Para tanto, será necessário construir um cadastro de clientes
que armazene as seguintes informações: cpf, nome, endereço, fone e e-mail; também será
necessário um cadastro de veículos que armazene as seguintes informações: placa, modelo, ano,
fabricante e cor. Para tanto, será necessário criar dois pacotes, um chamado modelo e outro,
controle.

40
No pacote modelo, coloque as classes modelo e, no pacote controle, coloque a classe que
conterá o método main. Você pode colocar o nome da classe como Principal, e a mesma servirá
para instanciar as classes modelo e iniciar o programa. Use uma linguagem de programação
orientada a objetos para executar a tarefa, crie um projeto e, depois de concluídas as atividades,
exporte o projeto no formato zip e envie o arquivo.

INFOGRÁFICO

Veja, no infográfico, uma descrição sobre escolha de classes, coesão, acoplamento e pacotes.

CONTEÚDO DO LIVRO

Escolher, nomear e estruturar bem as classes de uma aplicação, assim como organizá-las em
pacotes, é de extrema importância no paradigma de orientação a objetos. Portanto, o domínio
destes conceitos torna-se vital para o desenvolvimento de softwares.

Acompanhe um trecho do livro Conceitos de computação com Java; este livro serve de base
teórica para esta Unidade de Aprendizagem. Inicie sua leitura pelo tópico Escolhendo classes e
vá até o final do tópico Coesão e acoplamento.

41
CAY HORSTMANN

CONCEITOS DE
C O M P U TA Ç Ã O C O M
Compatível com
Java 5 & 6

Java
5a Edição

42
Conceitos de Computação com Java

8.1 Escolhendo classes


Você já utilizou um bom número de classes nos capítulos anteriores e provavelmente pro-
jetou algumas como parte de seus exercícios de programação. Projetar uma classe pode ser
um desafio – nem sempre é fácil dizer como começar ou se o resultado é de boa qualidade.
Estudantes com alguma experiência anterior em programação, em uma outra lingua-
gem, estão acostumados a programar funções. Uma função executa uma ação. Na pro-
gramação orientada a objetos, as ações aparecem como métodos. Cada método, porém,
pertence a uma classe. Classes são coleções de objetos e objetos não são ações – eles
são entidades. Portanto, você precisa começar a atividade de programação identificando
objetos e as classes às quais eles pertencem.
Lembre-se da regra geral do Capítulo 2: nomes de classe devem ser substantivos e
nomes de método devem ser verbos.
O que faz uma boa classe? Acima de tudo, uma classe deve
Uma classe deve representar um único conceito. Algumas classes que vimos repre-
representar um único
sentam conceitos matemáticos:
conceito do domínio do
problema, como negócios, • Point
ciência ou matemática.
• Rectangle
• Ellipse
Outras classes são abstrações de situações da vida real.
• BankAccount

• CashRegister

43
CAPÍTULO 8 䊏 Projetando Classes

Para essas classes, as propriedades de um objeto típico são fáceis de entender. Um objeto
Rectangle tem uma largura e uma altura. Dado um objeto BankAccount, você pode depo-
sitar e sacar dinheiro. Geralmente, conceitos da parte do universo compreendida pelo
programa, como ciências, negócios ou um jogo, fazem boas classes. O nome para essa
classe deve ser um substantivo que descreve o conceito. Alguns nomes de classes padrão
em Java são um pouco estranhos, como Ellipse2D.Double, mas você pode escolher no-
mes mais apropriados para suas classes.
Uma outra categoria útil de classe pode ser descrita como atores. Objetos de uma
classe de atores realizam alguns trabalhos para você. Exemplos de atores são a classe
Scanner do Capítulo 4 e a classe Random no Capítulo 6. Um objeto Scanner varre um fluxo
de dados em busca de números e strings. Um objeto Random gera números aleatórios. Se
utilizar a língua inglesa para atribuir nomes, uma boa idéia é escolher nomes de classes
para os atores que terminem em “er” ou “or”. (Um nome melhor para a classe Random
poderia ser RandomNumberGenerator.)
Ocasionalmente, uma classe não tem nenhum objeto, mas contém uma coleção de
constantes e métodos estáticos relacionados. A classe Math é um exemplo típico. Uma
classe assim é chamada de classe utilitária.
Por fim, você viu classes com somente um método main. O único propósito dessas
classes é iniciar um programa. Da perspectiva de projeto, estes são exemplos mais ou
menos degenerados de classes.
O que não poderia ser uma boa classe? Se você não pode afirmar a partir do nome da
classe o que um objeto da classe supostamente deve fazer, provavelmente você não está
no caminho certo. Por exemplo, sua lição de casa poderia solicitar para você escrever
um programa que imprima cheques de pagamento. Suponha que inicialmente você tente
projetar uma classe PaycheckProgram. O que um objeto dessa classe faria? Um objeto des-
sa classe teria que fazer tudo o que a lição de casa exige fazer. Isso não simplifica nada.
Uma classe melhor seria Paycheck. Seu programa pode então manipular um ou mais
objetos da classe Paycheck.
Um outro equívoco comum, especialmente de estudantes habituados a escrever
programas que consistem em funções, é transformar uma ação em uma classe. Por
exemplo, se sua lição de casa fosse calcular um cheque de pagamento, você poderia
considerar escrever uma classe ComputePaycheck. Mas você consegue visualizar um ob-
jeto do tipo ComputePaycheck? O fato de que ComputePaycheck não é um substantivo
leva você a achar que está no caminho errado. Por outro lado, uma classe Paycheck tem
um sentido intuitivo. O termo “cheque de pagamento” (ou Paycheck) é um substantivo.
Você pode visualizar um objeto de cheque de pagamento. Então você pode pensar em
métodos úteis para a classe Paycheck, como computeTaxes, que o ajudarão a resolver a
lição de casa.

AUTOVERIFICAÇÃO DA APRENDIZAGEM
1. Qual é a regra geral para escolher classes?
2. Seu trabalho é escrever um programa de xadrez. A classe ChessBoard (Tabuleiro)
poderia ser uma classe apropriada? Que tal MovePiece?

44
Conceitos de Computação com Java

8.2 Coesão e acoplamento


Nesta seção você aprenderá dois critérios úteis para analisar a qualidade da interface
pública de uma classe.
Uma classe deve representar um único conceito. As constantes
A interface pública de uma e os métodos públicos que a interface pública expõe devem ser co-
classe é coesa se todos os
esos. Isto é, todos os recursos da interface devem estar intimamente
seus recursos estiverem
relacionados ao conceito relacionados ao único conceito que a classe representa.
que a classe representa. Se achar que a interface pública de uma classe referencia vá-
rios conceitos, isso é um bom sinal de que pode ser a hora de utili-
zar classes separadas. Considere, por exemplo, a interface pública da classe CashRegister
no Capítulo 4:
public class CashRegister
{
public void enterPayment(int dollars, int quarters,
int dimes, int nickels, int pennies)
. . .
public static final double NICKEL_VALUE = 0.05;
public static final double DIME_VALUE = 0.1;
public static final double QUARTER_VALUE = 0.25;
. . .
}

Na verdade há dois conceitos aqui: uma caixa registradora que armazena moedas e cal-
cula o total e os valores das moedas individuais. (Por simplicidade, supomos que a caixa
registradora só contém moedas, não cédulas. O Exercício P8.1 discute uma solução mais
geral.)
Faz sentido ter uma classe Coin separada e tornar as moedas responsáveis pelos seus
valores.
public class Coin
{
public Coin(double aValue, String aName) { . . . }
public double getValue() { . . . }
. . .
}

CashRegister

Figura 1 Coin
Relacionamento de dependência
entre as classes CashRegister e Coin.

45
CAPÍTULO 8 䊏 Projetando Classes

A classe CashRegister pode então ser simplificada:


public class CashRegister
{
public void enterPayment(int coinCount, Coin coinType) { . . . }
. . .
}
Agora a classe CashRegister não precisa conhecer mais nada sobre os valores das moe-
das. A mesma classe pode muito bem tratar euros ou zorkmids!
Claramente essa é uma solução melhor, porque ela separa as responsabilidades entre
a caixa registradora e as moedas. A única razão por que não seguimos essa abordagem no
Capítulo 4 foi manter o exemplo da CashRegister simples.
Muitas classes precisam de outras classes para que possam fazer
Uma classe depende de o seu trabalho. Por exemplo, a classe CashRegister reestruturada ago-
outra classe se ela usa
ra depende da classe Coin para determinar o valor do pagamento.
objetos dessa classe.
Para visualizar os relacionamentos, como a dependência entre
as classes, programadores criam diagramas de classes. Neste livro, utilizamos a nota-
ção UML (“Unified Modeling Language”) para objetos e classes. UML é uma notação
para análise orientada a objetos cujo projeto foi criado por Grady Booch, Ivar Jacobson
e James Rumbaugh, três importantes pesquisadores em desenvolvimento de software
orientado a objetos. A notação UML distingue entre diagramas de objetos e diagramas de
classes. Em um diagrama de objetos os nomes das classes são sublinhados; em um dia-
grama de classes os nomes das classes não são sublinhados. Em um diagrama de classes,
você indica a dependência por meio de uma linha tracejada usando uma seta com a ponta
aberta apontando para a classe dependente. A Figura 1 mostra um diagrama de classes
que indica que a classe CashRegister depende da classe Coin.
Observe que a classe Coin não depende da classe CashRegister. Moedas não fazem
idéia de que são coletadas pela caixa registradora e elas podem realizar suas funções sem
nunca chamar um método qualquer na classe CashRegister.
Se muitas classes de um programa dependerem umas das outras, dizemos então que
o acoplamento entre as classes é alto. Inversamente, se houver poucas dependências entre
as classes, dizemos que o acoplamento é baixo (veja Figura 2).

Acoplamento alto Acoplamento baixo

Figura 2 Acoplamento alto e acoplamento baixo entre classes.

46
Conceitos de Computação com Java

Uma boa prática é Por que o acoplamento é importante? Se a classe Coin muda na
minimizar o acoplamento próxima distribuição do programa, todas as classes que dependem
(isto é, a dependência) entre dela podem ser afetadas. Se a alteração for drástica, todas as clas-
classes. ses associadas devem ser atualizadas. Além disso, se quisermos
utilizar uma classe em outro programa, teremos que levar junto
todas as classes das quais ela depende. Portanto, queremos remo-
ver o acoplamento desnecessário entre as classes.

AUTOVERIFICAÇÃO DA APRENDIZAGEM
3. Por que a classe CashRegister do Capítulo 4 não é coesa?
4. Por que a classe Coin não depende da classe CashRegister?
5. Por que o acoplamento deve ser minimizado entre as classes?

DICA DE QUALIDADE 8.1

Consistência
Nesta seção você aprendeu dois critérios para analisar a qualidade da interface pública
de uma classe. Você deve maximizar a coesão e eliminar o acoplamento desnecessá-
rio. Há um outro critério que gostaríamos que você prestasse atenção – consistência.
Quando você tem vários métodos, siga um esquema consistente para os nomes e parâ-
metros desses métodos. Isso é simplesmente um sinal de bom estilo.
Infelizmente, você pode localizar várias inconsistências na biblioteca padrão. Eis
um exemplo. Para mostrar uma caixa de diálogo de entrada, você chama
JOptionPane.showInputDialog(promptString)

Para mostrar uma caixa de diálogo de mensagem, você chama


JOptionPane.showMessageDialog(null, messageString)

O que é o parâmetro null? Sabemos que o método showMessageDialog precisa de um


parâmetro para especificar a janela pai ou null se nenhuma janela pai for requerida.
Mas o método showInputDialog não requer nenhuma janela pai. Por que essa incon-
sistência? Não há razão alguma. Seria mais fácil fornecer um método showMessage-
Dialog que espelhasse exatamente o método showInputDialog.
Inconsistências como essas não são uma falha fatal, mas são uma dor de cabeça,
especialmente porque podem ser facilmente evitadas.

47
DICA DO PROFESSOR

Acompanhe, no vídeo, mais conceitos de projetando classes.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Em orientação a objetos, como devemos começar as atividades de programação?

A) Identificando os atributos.

B) Identificando os métodos.

C) Identificando os requisitos funcionais da aplicação.

D) Identificando objetos e as classes às quais eles pertencem.

E) Identificando nomes de classes.

2) Marque a afirmativa correta:

A) Nomeamos uma classe utilizando um verbo que define o objetivo dessa classe.

B) Uma classe deve ser criada para representar vários conceitos do domínio do problema.

C) Se você não pode afirmar, a partir do nome da classe, o que um objeto da classe
supostamente deve fazer, provavelmente você não está no caminho certo.

48
D) Uma categoria útil de classes pode ser descrita como atores. Essas classes servem para
iniciar um programa.

E) Uma prática comum é nomear métodos com algum substantivo.

3) Referente a coesão e acoplamento, podemos afirmar que:

A) Uma classe coesa representa uma solução bem estruturada no que se refere à criação do
objeto.

B) A interface pública de uma classe é coesa se abrange todos os requisitos funcionais do


sistema.

C) Quando a interface pública de uma classe referencia vários conceitos, é um bom sinal de
que pode ser hora de utilizar classes separadas.

D) Acoplamento refere-se à dependência que as classes possuem em relação aos seus


métodos.

E) Se muitas classes de um programa dependerem umas das outras, dizemos, então, que o
acoplamento entre as classes é baixo.

4) O que é um pacote?

A) É uma forma de organizar os métodos.

B) É um modificador de acesso.

C) Servem para iniciar programas.

49
D) Criamos objetos a partir das definições de um pacote.

E) É um conjunto de classes relacionadas.

5) Analise o código abaixo:

/*
package media;
public class calcularMedia {

private double nota1;


private double nota2;
private double media;
private int matricula;
private String nome;

public void calcularMedia(double nota1, double nota{


this.nota1 = nota1;
this.nota2 = nota2;
media = (nota1 + nota2)/2;

public void cadastrarAluno(int cod, String matricula){


this.cod=cod;
this.matricula = matricula;
}

}
*/

É correto afirmar que:

A) A classe “calcularMedia” segue a regra geral para nomes de classes.


50
B) O método “calcularMedia” não irá executar a expressão aritmética.

C) Esta classe não está dentro de nenhum pacote.

D) Esta classe não apresenta coesão.

E) A classe está escrita totalmente correta.

NA PRÁTICA

Veja, na prática, a identificação de um objeto, sua classificação e implementação em uma


linguagem de programação.

Conteúdo interativo disponível na plataforma de ensino!

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Diferenças entre classe e objeto em Java

Conteúdo interativo disponível na plataforma de ensino!

O que são e para que servem as classes e objetos

Conteúdo interativo disponível na plataforma de ensino!

51
Classes com tipos genéricos

APRESENTAÇÃO

Tipos genéricos são muito usados nas APIs da linguagem java. Como exemplo, podemos citar
as coleções, as quais também podem ser implementadas em nossas aplicações.

Nesta Unidade de Aprendizagem veremos alguns conceitos sobre classes com tipos genéricos.
Trabalharemos um escopo básico, já que este assunto é muito extenso, mas é necessário que
todos os programadores tenham um conhecimento básico.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é um tipo genérico.


• Reconhecer classes genéricas.
• Construir uma aplicação utilizando tipos genéricos.

DESAFIO

Classes genéricas podem ter mais de um parâmetro de tipo, isto é, mais uma das facilidades que
os tipos genéricos nos proporcionam.
Você é analista/programador em uma fábrica de software e seu trabalho é entregar uma
aplicação de cadastro de clientes. Para isso, deve criar uma classe modelo e uma de controle
para instanciar a modelo, no entanto, deve utilizar as técnicas de tipos genéricos. O cadastro de
cliente deverá conter nome, sobrenome, idade, endereço e e-mail.

Para executar o desafio, utilize uma linguagem de programação orientada a objetos, um IDE
para auxiliar na organização do projeto (sugerido: netBeans ou Eclipse).

Crie um projeto com o nome de cadastro; dentro, crie um pacote com o mesmo nome, e, dentro
do pacote, crie as classes necessárias.

Observação: a classe controle, além de instanciar a classe modelo e inserir os valores, deverá

52
invocar um método para impressão do objeto na tela.

INFOGRÁFICO

Veja no infográfico os conceitos de classes com tipos genéricos.

CONTEÚDO DO LIVRO

Os genéricos adicionaram um elemento de sintaxe totalmente novo e causaram mudanças em


muitas das classes e métodos da API principal. Não é exagero dizer que sua inclusão
basicamente reformulou a natureza de Java. Acompanhe um trecho do livro Java para iniciantes,
o qual traz uma abordagem sobre tipos genéricos e é a base teórica para esta Unidade de
Aprendizagem. Inicie o estudo pelo tópico Fundamentos dos tipos genéricos e finalize ao final
de Tipos genéricos diferem de acordo com seus argumentos de tipo.

Boa leitura.

53
Capítulo 13 Tipos genéricos

Principais habilidades e conceitos


• Entender as vantagens dos tipos genéricos
• Criar uma classe genérica
• Aplicar parâmetros de tipo limitado
• Usar argumentos curingas
• Aplicar curingas limitados
• Criar um método genérico
• Criar um construtor genérico
• Criar uma interface genérica
• Utilizar tipos brutos
• Aplicar a inferência de tipos com o operador losango
• Entender a técnica erasure
• Evitar erros de ambiguidade
• Conhecer as restrições dos genéricos

.....................................................................................................................................

D esde sua versão original, muitos recursos novos foram adicionados a Java.
Todos melhoraram e expandiram seu escopo, mas o que teve impacto parti-
cularmente profundo e extenso foi o tipo genérico, porque seus efeitos foram sen-
tidos em toda a linguagem. Por exemplo, os genéricos adicionaram um elemento
de sintaxe totalmente novo e causaram mudanças em muitas das classes e métodos
da API principal. Não é exagero dizer que sua inclusão basicamente reformulou a
natureza de Java.
O tópico “genéricos” é muito extenso e parte dele é avançado demais para
entrar no escopo deste livro. No entanto, um conhecimento básico dos genéricos é
necessário a todos os programadores Java. À primeira vista, a sintaxe dos genéricos
pode parecer um pouco complicada, mas não se preocupe, os genéricos são muito
fáceis de usar. Quando você terminar este capítulo, terá uma noção dos conceitos-
-chave que estão por trás dos genéricos e terá conhecimento suficiente para usá-los
de maneira eficaz em seus próprios programas.

Fundamentos dos tipos genéricos


Na verdade, com o termo genéricos queremos nos referir aos tipos parametrizados.
Os tipos parametrizados são importantes porque nos permitem criar classes, inter-

54
Java para Iniciantes

Pergunte ao especialista
P: Ouvi dizer que os genéricos Java são semelhantes aos templates de C++. É isso
mesmo?
R: Sim, os genéricos Java são semelhantes aos templates de C++. O que Java chama de
tipo parametrizado, C++ chama de template. No entanto, os genéricos Java e os tem-
plates C++ não são iguais e há algumas diferenças básicas entre as duas abordagens
de tipos genéricos. Geralmente, a abordagem Java é mais fácil de usar.
Uma advertência: se você tiver experiência em C++, é importante não tirar con-
clusões precipitadas sobre como os genéricos funcionam em Java. As duas abordagens
de código genérico diferem de maneiras sutis, mas básicas.

faces e métodos em que o tipo de dado usado é especificado como parâmetro. Uma
classe, interface ou método que opera sobre um parâmetro de tipo é chamado de
genérico, como em classe genérica ou método genérico.
Uma vantagem importante do código genérico é que ele funciona automatica-
mente com o tipo de dado passado para seu parâmetro de tipo. Muitos algoritmos são
logicamente iguais, não importando o tipo de dado ao qual estão sendo aplicados.
Por exemplo, uma classificação rápida é igual classificando itens de tipo Integer,
String, Object ou Thread. Com os genéricos, você pode definir um algoritmo uma
única vez, independentemente do tipo de dado, e então aplicá-lo a uma ampla varie-
dade de tipos de dados sem nenhum esforço adicional.
É importante entender que Java sempre permitiu a criação de classes, interfaces
e métodos generalizados usando referências de tipo Object. Já que Object é a super-
classe de todas as outras classes, uma referência Object pode referenciar qualquer
tipo de objeto. Logo, em códigos anteriores aos genéricos, classes, interfaces e méto-
dos generalizados usavam referências Object para operar com vários tipos de dados.
O problema é que eles não faziam isso com segurança de tipos, já que coerções
eram necessárias para converter explicitamente Object no tipo de dado que estava
sendo tratado. Portanto, era possível gerar acidentalmente discrepâncias de tipo. Os
genéricos adicionam a segurança de tipos que estava faltando, porque tornam essas
coerções automáticas e implícitas. Resumindo, eles expandem nossa habilidade de
reutilizar código e nos permitem fazê-lo de maneira segura e confiável.

Exemplo simples de genérico


Antes de discutir mais teoria, é melhor examinarmos um exemplo simples de genéri-
co. O programa a seguir define duas classes. A primeira é a classe genérica Gen e a
segunda é GetDemo, que usa Gen.
// Classe genérica simples.
// Aqui, T é um parâmetro de tipo que
// será substituído pelo tipo real quando
// um objeto de tipo Gen for criado.
class Gen<T> { Declara uma classe genérica. T é o
T ob; // declara um objeto de tipo T parâmetro de tipo genérico.

55
Capítulo 13 Tipos genéricos

// Passa para o construtor uma


// referência a um objeto de tipo T
Gen(T o) {
ob = o;
}

// Retorna ob.
T getob() {
return ob;
}

// Exibe o tipo de T.
void showType() {
System.out.println("Type of T is " +
ob.getClass().getName());
}
}

// Demonstra a classe genérica.


class GenDemo {
public static void main(String args[]) {
// Cria uma referência Gen para Integers.
Gen<Integer> iOb; Cria uma referência a um
objeto de tipo Gen<Integer>.
// Cria um objeto Gen<Integer> e atribui sua
// referência a iOb. Observe o uso do autoboxing no
// encapsulamento do valor 88 dentro de um objeto Integer.
iOb = new Gen<Integer>(88); Instancia um objeto
de tipo Gen<Integer>.
// Exibe o tipo de dado usado por iOb.
iOb.showType();

// Obtém o valor de iOb. Observe


// que nenhuma coerção é necessária.
int v = iOb.getob();
System.out.println("value: " + v); Cria uma referência e um
objeto de tipo Gen<String>.
System.out.println();

// Cria um objeto Gen para Strings.


Gen<String> strOb = new Gen<String>("Generics Test");

// Exibe o tipo de dado usado por strOb.


strOb.showType();

// Obtém o valor de strOb. Novamente, observe


// que nenhuma coerção é necessária.
String str = strOb.getob();
System.out.println("value: " + str);
}
}

56
Java para Iniciantes

A saída produzida pelo programa é mostrada abaixo:


Type of T is java.lang.Integer
value: 88

Type of T is java.lang.String
value: Generics Test

Examinemos esse programa com detalhes. Primeiro, observe como Gen é de-
clarada pela linha a seguir:
class Gen<T> {

Aqui, T é o nome de um parâmetro de tipo. Esse nome é usado como espaço reser-
vado para o tipo real que será passado para Gen quando um objeto for criado. Logo,
T será usado dentro de Gen sempre que o parâmetro de tipo for necessário. Observe
que T está dentro de < >. Essa sintaxe pode ser generalizada. Sempre que um pa-
râmetro de tipo estiver sendo declarado, ele será especificado dentro de colchetes
angulares (< >). Já que Gen usa um parâmetro de tipo, é uma classe genérica.
Na declaração de Gen, não há um significado especial no nome T. Qualquer
identificador válido poderia ter sido usado, mas o uso de T é tradicional. Além disso,
é recomendável que os nomes dos parâmetros de tipo tenham apenas um caractere:
uma letra maiúscula. Outros nomes de parâmetros de tipo normalmente usados são
V e E.
Em seguida, T é usado para declarar um objeto chamado ob, como mostrado
abaixo:
T ob; // declara um objeto de tipo T

Como explicado, T é um espaço reservado para o tipo real que será especificado
quando um objeto Gen for criado. Logo, ob será um objeto do tipo passado para T.
Por exemplo, se o tipo String for passado para T, então, nesse caso, ob será de tipo
String.
Agora, considere o construtor de Gen:
Gen(T o) {
ob = o;
}

Observe que seu parâmetro, o, é de tipo T. Ou seja, o tipo real de o será determinado
pelo tipo passado para T quando um objeto Gen for criado. Além disso, já que tanto
o parâmetro o quanto a variável membro ob são de tipo T, ambos terão o mesmo tipo
quando da criação de um objeto Gen.
O parâmetro de tipo T também pode ser usado para especificar o tipo de retor-
no de um método, como ocorre com o método getob( ), mostrado aqui:
T getob() {
return ob;
}

Já que ob também é de tipo T, seu tipo é compatível com o tipo de retorno especifi-
cado por getob( ).

57
Capítulo 13 Tipos genéricos

O método showType( ) exibe o tipo de T. Ele faz isso chamando getName( )


no objeto Clas retornado pela chamada a getClass( ) em ob. Não usamos esse recur-
so antes, logo, vamos examiná-lo em detalhes. Você deve lembrar que, no Capítulo
7, vimos que a classe Object define o método getClass( ). Portanto, getClass( ) é
membro de todos os tipos de classe. Ele retorna um objeto Class correspondente ao
tipo de classe do objeto em que foi chamado. Class é uma classe definida dentro de
java.lang que encapsula informações sobre outra classe. Ela define vários métodos
que podem ser usados na obtenção de informações sobre uma classe no tempo de
execução. Entre eles, está o método getName( ), que retorna uma representação do
nome da classe na forma de string.
A classe GenDemo demonstra a classe genérica Gen. Primeiro, ela cria uma
versão de Gen para inteiros, como vemos abaixo:
Gen<Integer> iOb;

Examine bem essa declaração. Primeiro, observe que o tipo Integer é especificado
dentro de colchetes angulares após Gen. Nesse caso, Integer é um argumento de tipo
que é passado para o parâmetro de tipo de Gen, que é T. Isso cria uma versão de Gen
em que todas as referências a T são convertidas para referências a Integer. Logo,
para essa declaração, ob é de tipo Integer e o tipo de retorno de getob( ) também.
Antes de prosseguirmos, é preciso dizer que o compilador Java não cria real-
mente versões diferentes de Gen ou de qualquer outra classe genérica. Embora seja
útil pensar assim, não é o que acontece. Em vez disso, o compilador remove todas as
informações do tipo genérico, substituindo pelas coerções necessárias, para fazer o có-
digo se comportar como se uma versão específica de Gen fosse criada. Logo, na ver-
dade, há apenas uma versão de Gen no programa. O processo de remover informações
do tipo genérico se chama erasure e ele será discutido posteriormente neste capítulo.
A próxima linha atribui a iOb uma referência a uma instância de uma versão
Integer da classe Gen.
iOb = new Gen<Integer>(88);

Observe que quando o construtor de Gen é chamado, o argumento de tipo Integer


também é especificado. Isso é necessário porque o objeto (nesse caso, iOb) ao qual a
referência está sendo atribuída é de tipo Gen<Integer>. Logo, a referência retornada
por new também deve ser de tipo Gen<Integer>. Se não for, ocorrerá um erro de
tempo de compilação. Por exemplo, a atribuição a seguir causará um erro de tempo
de compilação:
iOb = new Gen<Double>(88.0); // Erro!

Já que iOb é de tipo Gen<Integer>, não pode ser usada para referenciar um objeto
de Gen<Double>. Esse tipo de verificação é um dos principais benefícios dos gené-
ricos porque assegura a segurança dos tipos.
Como os comentários do programa informam, a atribuição
iOb = new Gen<Integer>(88);

faz uso do autoboxing para encapsular o valor 88, que é um int, em um Integer.
Isso funciona porque Gen<Integer> cria um construtor que recebe um argumento

58
Java para Iniciantes

Integer. Já que um Integer é esperado, Java encapsulará automaticamente 88 dentro


dele. É claro que a atribuição também poderia ter sido escrita explicitamente, da
seguinte forma:
iOb = new Gen<Integer>(new Integer(88));

No entanto, não teríamos vantagem usando essa versão.


Em seguida, o programa exibe o tipo de ob dentro de iOb, que é Integer. De-
pois, obtém o valor de ob usando a linha abaixo:
int v = iOb.getob();

Como o tipo de retorno de getob( ) é T, que foi substituído por Integer quando iOb
foi declarada, ele também é Integer, que é encapsulado em int quando atribuído a v
(que é um int). Logo, não há necessidade de converter o tipo de retorno de getob( )
para Integer.
Agora, GenDemo declara um objeto de tipo Gen<String>:
Gen<String> strOb = new Gen<String>("Generics Test");

Como o argumento de tipo é String, T é substituído por String dentro de Gen. Isso
cria (conceitualmente) uma versão String de Gen, como as linhas restantes do pro-
grama demonstram.

Genéricos só funcionam com tipos de referência


Na declaração de uma instância de um tipo genérico, o argumento de tipo passado
para o parâmetro de tipo deve ser um tipo de referência. Você não pode usar um tipo
primitivo, como int ou char. Por exemplo, com Gen, é possível passar qualquer tipo
de classe para T, mas você não pode passar um tipo primitivo para T. Logo, a decla-
ração a seguir é inválida:
Gen<int> intOb = new Gen<int>(53); // Erro, não pode usar um tipo primitivo

Certamente, não poder especificar um tipo primitivo não é uma restrição grave, por-
que você pode usar os encapsuladores de tipos (como fez o exemplo anterior) para
encapsular um tipo primitivo. Além disso, o mecanismo Java de autoboxing e autou-
nboxing torna o uso do encapsulador de tipos transparente.

Tipos genéricos diferem de acordo com seus


argumentos de tipo
Um ponto-chave que devemos entender sobre os tipos genéricos é que uma referên-
cia de uma versão específica de um tipo genérico não tem compatibilidade de tipo
com outra versão do mesmo tipo genérico. Por exemplo, supondo o programa que
acabei de mostrar, a linha de código abaixo está errada e não será compilada:
iOb = strOb; // Errado!

Ainda que tanto iOb quanto strOb sejam de tipo Gen<T>, são referências a tipos
diferentes porque seus argumentos de tipo diferem. Isso faz parte da maneira como
os genéricos adicionam segurança de tipos e evitam erros.

59
DICA DO PROFESSOR

Veja no vídeo a seguir os conceitos de classes com tipos genéricos.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Em relação a tipos genéricos, marque a alternativa INCORRETA.

A) O tópico genérico é extenso.

B) Genéricos não são tipos primitivos de dados.

C) Quando declaramos um atributo como String, estamos declarando como genérico.

D) Com um genérico você pode definir um algoritmo apenas uma vez, independentemente do
tipo de dados.

E) Com genéricos, podemos reutilizar código.

2) Nos fundamentos dos tipos genéricos, quando utilizamos o termo “genérico”, estamos
nos referindo a:

A) Estrutura de herança.

B) Um tipo primitivo de dados que pode receber qualquer valor.

60
C) Quando uma aplicação pode ser executada sem o método main em java.

D) Usamos variáveis de referência ao instanciarmos uma classe.

E) Quando nos referimos a tipos parametrizados.

3) Para declararmos uma classe que utilizará tipos genéricos, utilizamos qual sintaxe?

A) class NomeDaClasse{ //implementação. }

B) class abstract NomeDaClasse{ //implementação. }

C) class NomeDaClasse extends Generica{ //implementação. }

D) class NomeDaClasse implements Generica{ //implementação. }

E) class NomeDaClasse <Parâmetro de tipo>{ //implementação. }

4) Analise o código e marque a alternativa INCORRETA.

1 - class Gen<T>{
2 - T ob;
3-
4 - Gen(T o) {
5-
6- ob = o;
7-
8- }
9-
10 - T getob(){
11 - return ob;

61
12 - }
13 -
14 - void showType() {
15 -
16 - System.out.println("Type of T is " + ob.getClass().getName());
17 -
18 - }
19 - }
20 -
21 -
22 - //Demonstra a classe genérica.
23 -
24 - class GenDemo {
25 -
26 - public static void main(String[] args){
27 -
28 - Gen<Integer> iOb = new Gen<Integer>(88);
29 -
30 - iOb.showType();
31 -
32 - int v = iOb.getob();
33 -
34 - System.out.println("Value: " + v);
35 -
36 - System.out.println();
37 -
38 - Gen<String> strOb = new Gen<String>("Generics Test");
39 -
40 - strOb.showType();
41 -
42 - String str = strOb.getob();
43 -
44 - System.out.println("Value: "+ str);
45 -
46 - }
47 - }

62
A) Veja o trecho de código escrito na linha 1. class Gen<T>{ A letra T, representa o nome de
um parâmetro de tipo.

B) Veja o trecho de código escrito na linha 2. T ob; Teremos um erro de compilação, pois não
estamos definindo um tipo válido para o atributo ob.

C) Veja o trecho de código escrito nas linhas 10,11 e 12. T getob(){ return ob; } Temos o
método getob() que retorna um tipo T.

D) Veja o trecho de código escrito na linha 28. Gen iOb = new Gen(88); Estamos criando
uma versão de Gen para inteiros e atribuindo uma referência a iOb a uma instância da
classe GEN.

E) Veja o trecho de código escrito na linha 38. Gen strOb = new Gen("Generics Test"); Aqui,
GenDemo está declarando um objeto do tipo Gen.

5) Marque a alternativa INCORRETA.

A) Genéricos só funcionam com tipos de referência.

B) Tipos genéricos diferem de acordo com seus argumentos de tipo.

C) Classes genéricas podem ser declaradas com mais de um parâmetro.

D) É recomendável que os nomes de parâmetros de tipos tenham apenas uma letra.

E) As letras a serem utilizadas como nomes de parâmetros de tipos devem ser apenas “T”,
“E” ou “V”.

NA PRÁTICA

63
Veja no vídeo uma aplicação prática de classes com tipos genéricos.

Conteúdo interativo disponível na plataforma de ensino!

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Você é realmente um bom desenvolvedor Java?

Para ser um bom desenvolvedor Java é preciso, acima de tudo, ter experiência. Nesse artigo, leia
mais sobre o assunto.

Conteúdo interativo disponível na plataforma de ensino!

Artigo Introdução: Sobrecarga de Métodos e Tipos Genéricos em Java

eja nesse artigo como e quando utilizar sobrecarga de métodos e tipos genéricos e quais
problemas cada um ajuda a resolver.

Conteúdo interativo disponível na plataforma de ensino!

Conceitos de Computação com Java - Compatível com Java 5 & 6

Cay Horstmann, 2009, 5ª Edição

64
Interfaces

APRESENTAÇÃO

Em orientação a objetos, o conceito de interface refere-se a um modelo a ser seguido. Interface é


uma classe que possui métodos não implementados, entretanto, ao passo que declaramos a
palavra-chave class para classes, declaramos interface para interface. Podemos exemplificar
interface como sendo um contrato, e outras classes podem assinar este contrato se
responsabilizando em implementar esses métodos.

Nesta Unidade de Aprendizagem conheceremos um pouco mais sobre interfaces e sua aplicação.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é interface.


• Identificar herança e implementação de interface.
• Construir uma aplicação usando interfaces.

DESAFIO

As interfaces podem ser utilizadas até mesmo em tarefas mais simples. Além de definirem
métodos a serem utilizados pelas classes que as implementam, as interfaces podem declarar
constantes para serem usadas por estas classes.

Você trabalha na área de TI de uma escola. A secretaria da instituição solicitou que você crie
uma forma de inserir o conceito obtido pelo aluno, com sua descrição, de tal maneira que apenas
seja necessário informar o número do conceito obtido.

Você deverá criar uma interface com três constantes, constante "a" = "Parabéns, você atingiu
todos os indicadores de avaliação com excelência"; constante "b" = "Parabéns, você obteve
aproveitamento satisfatório nos indicadores de avaliação"; constante "c" = "Você não atingiu o
mínimo esperado para aprovação".

65
Crie uma classe para implementar a interface. Nesta classe, crie uma variável para receber a
opção digitada pelo usuário. Utilize o método showInputDialog() da classe JOptionPane para
solicitar as opções. Crie uma estrutura de decisão para verificar a opção digitada e printar na tela
o conceito e sua descrição.

Utilize uma linguagem de programação orientada a objetos. Crie um projeto chamado


ConceitoAluno; dentro dele, crie um pacote chamado interfaces, e, dentro do pacote, crie a
interface e a classe para implementá-la.

INFOGRÁFICO

Veja no infográfico os conceitos de interface e herança.

CONTEÚDO DO LIVRO

66
Interfaces definem tipos em uma forma abstrata, como uma coleção de métodos ou outros tipos
que formam o contrato para aquele tipo. Interfaces não contêm implementações e você não pode
criar instâncias de uma interface. Acompanhe um trecho do livro A linguagem de programação
java, livro que serve de base teórica para esta Unidade de Aprendizagem. Inicie o estudo pelo
tópico Interfaces e finalize no tópico Estendendo Interfaces.

67
68
CAPÍTULO 4
Interfaces

“Reger” é quando você desenha “figuras” em nenhum lugar – com sua batuta
ou com suas mãos – que são interpretadas como “mensagens instrucionais”
por rapazes vestindo gravatas borboleta que desejariam estar pescando.
– Frank Zappa

A unidade fundamental de programação na linguagem de programação Java é a clas-


se, mas a unidade fundamental de projeto orientado a objetos é o tipo. Enquanto clas-
ses definem tipos, é bastante útil e poderoso poder definir um tipo sem definir uma
classe. Interfaces definem tipos em uma forma abstrata como uma coleção de métodos
ou outros tipos que formam o contrato para aquele tipo. Interfaces não contêm imple-
mentações e você não pode criar instâncias de uma interface. Em vez disso, classes po-
dem expandir seus próprios tipos pela implementação de uma ou mais interfaces. Uma
interface é uma expressão de projeto puro, enquanto que uma classe é uma mistura de
projeto e implementação.
Uma classe pode implementar os métodos de uma interface de qualquer maneira que o
projetista da classe escolher. Uma interface, portanto possui muitas mais possíveis imple-
mentações do que uma classe. Cada classe principal em uma aplicação deve ser uma im-
plementação de alguma interface que captura o contrato daquela classe.
Classes podem implementar mais de uma interface. A linguagem de programação Java
permite herança múltipla de interface, mas somente herança simples de implementação –
uma classe pode estender somente uma outra classe. Classes podem usar herança de in-
terfaces para expandir seu tipo e então usar, por exemplo, composição para fornecer uma
implementação para estas interfaces. Este projeto permite a flexibilidade de tipo da he-
rança múltipla ao mesmo tempo em que evita os perigos da herança múltipla de imple-
mentação, ao custo de algum trabalho adicional para o programador.
Em uma dada classe, as classes que são estendidas e as interfaces que são implementa-
das são coletivamente chamadas de supertipos, e, do ponto de vista dos supertipos, uma
nova classe é um subtipo. A nova classe inclui todos os seus supertipos, de modo que a
referência a um objeto do subtipo pode ser usada polimorficamente em qualquer lugar
onde uma referência a um objeto de qualquer de seus supertipos (classe ou interface) é
requerida. Declarações de interfaces criam nomes de tipos assim como fazem as decla-
rações de classes; você pode usar o nome de uma interface como o nome de tipo de
uma variável, e qualquer objeto cuja classe implemente esta interface pode ser atribuí-
do a esta variável.

69
A LINGUAGEM DE PROGRAMAÇÃO JAVA

4.1 Um Exemplo de uma Interface Simples


Muitas interfaces simples definem uma propriedade que é atribuível a diversos objetos de
diferentes classes. Estas propriedades são frequentemente definidas em termos de um ob-
jeto estar “apto” a fazer algo. Por exemplo, nos pacotes padrões existem diversas interfa-
ces de “habilidade”, tais como;
• Cloneable – objetos deste tipo suportam clonagem, como você aprendeu com
detalhes na página 114.
• Comparable – objetos deste tipo possuem um ordenamento que permite que se-
jam comparados.
• Runnable – objetos deste tipo representam uma unidade de trabalho, que muitas ve-
zes podem ser executadas em um fluxo de controle independente (ver Capítulo 14).
• Serializable – objetos deste tipo podem ser escritos em um stream de bytes de
objetos para serem remetidos para uma nova máquina virtual, ou para armazena-
mento persistente e posterior reconstituição para um objeto vivo (ver “Serializa-
ção de Objetos” nas páginas 492-493).
Vamos examinar com mais detalhes a interface Comparable. Esta interface pode ser im-
plementada por qualquer classe cujos objetos podem ser comparados uns com os outros de
acordo com o “ordenamento natural” da classe. A interface contém um único método:
public interface Comparable<T> {
int compareTo(T obj);
}

Uma declaração de interface é similar a uma declaração de classe, exceto que a palavra-
chave interface é usada em lugar de class. Também existem regras especiais que go-
vernam os membros de uma interface, como em breve você vai aprender.
O método compareTo usa como argumento um único objeto do tipo T e o compara ao
objeto atual (esperado ser também do tipo T), retornando um inteiro negativo, nulo ou
positivo, se o objeto atual é menor do que, igual a ou maior do que o argumento, res-
pectivamente.
Considere uma variação da classe Point que introduzimos no Capítulo 1. O ordenamen-
to natural para pontos poderia ser a sua distância da origem. Poderíamos então tornar
Comparable objetos Point:
class Point implements Comparable<Point> {

/** Referência da origem que nunca muda */


private static final Point ORIGIN = new Point();

private int x, y;

//... definição de construtores, métodos de configuração e


de acesso

public double distance(Point p) {


int xdiff = x – p.x;

70
CAPÍTULO 4 • INTERFACES

int ydiff = y – p.y;


return Math.sqrt(xdiff * xdiff + ydiff * ydiff);
}

public int compareTo(Point p) {


double pDist = p.distance(ORIGIN);
double dist = this.distance(ORIGIN);
if (dist > pDist)
return 1;
else if (dist == pDist)
return 0;
else
return -1;
}
}
Primeiro declaramos que Point é uma classe Comparable. A classe identifica os ti-
pos de interface que ela implementa relacionando-os após a palavra-chave imple-
ments, antes que o corpo da classe seja definido (e após a cláusula extends). Todas
estas interfaces são as superinterfaces da classe. A classe deve providenciar uma imple-
mentação para todos os métodos definidos em suas superinterfaces, ou senão a classe
deve ser declarada como abstract, e assim exigindo que qualquer subclasse não abs-
trata os implemente.
O único método que necessitamos implementar para a interface Comparable é compa-
reTo, e para realmente comparar dois pontos simplesmente comparamos as suas distân-
cias em relação à origem.
Interfaces introduzem nomes de tipos assim como fazem as classes, de modo que você
pode declarar variáveis destes tipos. Por exemplo:
Comparable<Point> p1;
De fato, muito do poder das interfaces provém da declaração e uso de variáveis somente
do tipo da interface, em vez de algum tipo específico de classe. Por exemplo, você pode
definir uma rotina sort de uso geral, que pode ordenar qualquer array de objetos Com-
parable sem se preocupar com o que a classe desses objetos é na realidade – todos os
objetos do array devem, naturalmente, ser do mesmo tipo:1
class Sorter {
static Comparable<?>[] sort(Comparable<?>[] list) {
// detalhes de implementação...
return list;
}
}
Referências do tipo interface, entretanto, podem ser usadas somente para acessar mem-
bros daquela interface. Por exemplo, o seguinte irá produzir um erro de compilação:
Comparable<Point> obj = new Point();
double dist = obj.distance(p1); // INVÁLIDO: Comparable não
// possui método distance

1
No capítulo 11 você aprende sobre métodos genéricos, e sort realmente deveria ser definido como um desses.

71
A LINGUAGEM DE PROGRAMAÇÃO JAVA

Se você quiser tratar obj como um objeto Point você deve fazer uma coerção explícita pa-
ra este tipo.
Você pode invocar quaisquer dos métodos de Object usando uma referência do tipo de uma
interface porque não importa quais interfaces o objeto implemente, ele sempre será um Ob-
ject e assim possui aqueles métodos. De fato, qualquer interface que não estende outra in-
terface explicitamente possui membros que são os métodos públicos de Object (a menos
que a interface os sobrescreva explicitamente). Portanto, o seguinte é legal:
String desc = obj.toString();
como uma atribuição de uma referência de interface a uma referência de Object.

4.2 Declarações de Interfaces


Uma interface é declarada usando a palavra-chave interface, fornecendo um nome à
interface e relacionando os membros da interface entre chaves.
Uma interface pode declarar três tipos de membros:
• constantes (campos)
• métodos
• classes e interfaces aninhadas
Todos os membros da interface são implicitamente públicos, mas, por convenção, o mo-
dificador public é omitido. Ter membros não públicos em uma interface teria pouco
sentido; onde isto faz sentido você pode usar a acessibilidade da própria interface para
controlar o acesso aos membros da interface.
Vamos postergar a discussão de classes e interfaces aninhadas até o Capítulo 5.

4.2.1 Constantes de Interfaces


Uma interface pode declarar constantes denominadas. Estas constantes são definidas co-
mo campos, mas são implicitamente public, static e final – novamente, por con-
venção, os modificadores são omitidos nas declarações de campos. Estes campos devem
ter inicializadores – brancos finais não são permitidos. Anotações também podem ser
aplicadas aos campos – ver Capítulo 15.
Visto que interfaces não contêm detalhes de implementação, elas não podem definir cam-
pos normais – tal definição estaria ditando uma política de implementação para classes
que escolhessem implementar a interface. Interfaces podem definir constantes denomina-
das porque elas são úteis no projeto de tipos. Por exemplo, uma interface que possui di-
ferentes níveis de verbosidade em seu contrato pode definir o seguinte:
interface Verbose {
int SILENCIOSA = 0;
int CONCISA = 1;
int NORMAL = 2;
int PROLIXA = 3;

void setVerbosity(int level);


int getVerbosity();
}

72
CAPÍTULO 4 • INTERFACES

SILENCIOSA, CONCISA, NORMAL e PROLIXA podem ser passadas ao método setVerbo-


sity, dando nomes a valores constantes que representam significados específicos. Nes-
te caso particular, os níveis de verbosidade podem ser representados mais efetivamente
por constantes de um tipo enumerado aninhado dentro da interface Verbose. Tipos enu-
merados são discutidos no Capítulo 6.
Se você necessita dados compartilhados e modificáveis em sua interface, você pode con-
seguir isto usando uma constante denominada que se refere a um objeto que contém o da-
do. Uma classe aninhada é boa para definir tal objeto, de modo que postergaremos um
exemplo até o Capítulo 5.

4.2.2 Métodos de Interfaces


Os métodos declarados em uma interface são implicitamente abstratos porque nenhuma
implementação é, ou pode ser, dada a eles. Por esta razão, o corpo do método é simples-
mente um ponto-e-vírgula após o cabeçalho do método. Por convenção, o modificador
abstract é omitido na declaração do método.
Nenhum outro modificado, de método é permitido em uma declaração de método de in-
terface, exceto para anotações – ver Capítulo 15. Eles são implicitamente public e as-
sim não podem ter nenhum outro modificador de acesso. Eles não podem ter modificado-
res que definem características de implementação – tais como native, synchronized
ou strictfp – porque uma interface não dita implementação e eles não podem ser fi-
nal porque ainda não foram implementados. Naturalmente, a implementação destes mé-
todos dentro de uma classe específica pode ter qualquer um destes modificadores. Méto-
dos de interfaces nunca podem ser métodos static porque métodos static não podem
ser abstract.

4.2.3 Modificadores de Interfaces


Uma declaração de interface pode ser precedida de modificadores de interface:
• anotações – anotações e tipos de anotação são discutidos no Capítulo 15.
• pública – uma interface public é publicamente acessível. Sem este modificador,
uma interface somente é acessível dentro de seu próprio pacote.
• abstratas – todas as interfaces são implicitamente abstract porque seus méto-
dos são todos abstratos – eles não possuem implementação. Novamente, por con-
venção, o modificador abstract é sempre omitido.
• ponto-flutuante estrito – uma interface declarada strictfp possui toda a
aritmética de ponto-flutuante, definida dentro da interface, avaliada estrita-
mente. Isto se aplica a expressões de inicialização para constantes e para to-
dos os tipos aninhados declarados na interface. Em contraste com classes, is-
to não implica que cada método na interface seja implicitamente strictfp,
porque este é um detalhe de implementação. Ver Seção 9.1.3 nas páginas 199-
200 para detalhes.
Quando diversos modificadores são aplicados à mesma interface, recomendamos usar
a ordem relacionada acima.

73
A LINGUAGEM DE PROGRAMAÇÃO JAVA

4.3 Estendendo Interfaces


Interfaces podem ser estendidas usando a palavra-chave extends. Interfaces, diferente-
mente de classes, podem estender mais de uma interface:
public interface SerializableRunnable
extends java.io.Serializable, Runnable
{
//...
}
A interface SerializableRunnable estende java.io.Serializable e Runna-
ble ao mesmo tempo, o que significa que todos os métodos e constantes definidos nes-
tas interfaces agora fazem parte do contrato de SerializableRunnable, juntamen-
te com todos os novos métodos e constantes que ela define. As interfaces que são esten-
didas são as superinterfaces da nova interface e a nova interface é uma subinterface de
suas superinterfaces.
Visto que interfaces suportam herança múltipla, o grafo de herança pode conter diversos
caminhos para a mesma superinterface. Isto significa que constantes e métodos podem
ser acessados por diversos caminhos. Entretanto, visto que interfaces não definem imple-
mentação de métodos e não fornecem campos de objetos, não existem dificuldades con-
cernentes a esta forma de herança múltipla.

74
DICA DO PROFESSOR

Assista ao vídeo a seguir e veja a implementação de uma interface.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Para que possamos ter um melhor entendimento do conceito de interfaces, é


importante termos um conhecimento sobre a unidade fundamental em linguagem de
programação orientado a objeto. Marque a afirmativa que cita esta unidade.

A) Método.

B) Atributos.

C) Modificadores de acesso.

D) Pacote.

E) Classes.

2) Marque a afirmativa que define interface.

A) São formulários que interagem com o usuário.

B) Interface define tipos em forma abstrata.

75
C) São elementos da classe.

D) Interface serve para organizar classes de uma aplicação.

E) Interfaces são classes que possuem apenas métodos e podem ser instanciadas.

3) Em uma estrutura de herança, uma classe usa membros de outra classe. Interfaces
fornecem membros para que outras classes possam fazer uso deles. Com isso, é
importante conhecermos o conceito de herança para podermos analisar suas
diferenças com implementação de interfaces. Marque a alternativa que melhor define
herança.

A) Herança é uma estrutura que possui uma superclasse e subclasses que herdam membros
desta superclasse.

B) Em uma estrutura de herança, as subclasses herdam apenas métodos da superclasse.

C) Ao herdar um método da superclasse, ele não poderá ser sobrescrito pela superclasse.

D) Uma superclasse não pode ser instanciada.

E) Para criarmos uma estrutura de herança, devemos limitar a duas o número de subclasses.

4) Queremos criar uma interface com o nome Contrato e com o método sem retorno
chamado entrarComTexto(). Marque a afirmativa que escreve corretamente o
código.

A) public class Contrato{ void entrarComTexto(String texto); }

B) public abstract class Contrato{ void entrarComTexto(String texto); }

76
C) public Interface class Contrato{ void entrarComTextoString texto;}

D) public interface Contrato{ void entrarComTexto(String texto); }

E) public interface Contrato{ void entrarComTexto(String texto){ text = texto;


System.out.println(texto); } }

5) Uma classe que implementa uma interface assume a responsabilidade de executar as


ações que a interface define. Dessa forma, para podermos obter as assinaturas dos
métodos da interface em uma classe, considere os códigos abaixo e marque a
afirmativa correta. A é nossa classe e B é nossa interface.

A) public class A extends B{ }

B) public implements A class B { }

C) public class A extends C implements B { }

D) public class A implements B { }

E) public interface B { }

NA PRÁTICA

Interface é um recurso da orientação a objetos muito utilizado em Java, para obrigar as classes
que a implementam a utilizarem seus métodos.

Podemos fazer uma analogia a um contrato de prestação de serviços:

77
Em Java, utilizamos interfaces como uma forma de criar heranças múltiplas, já que Java não
permite tal estrutura. Com isso, as classes podem herdar membros de uma superclasse e
implementar de quantas interfaces forem necessárias. O uso de interfaces em um projeto pode
ser uma forma de obrigar o programador a usar determinados métodos nas classes. Os métodos
de uma interface devem ser sobrescritos nas classes que as implementam.

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Java interface: aprenda a usar corretamente.

Leia o artigo abaixo e conheça mais a respeito do uso de Interfaces em Java.

Conteúdo interativo disponível na plataforma de ensino!

Java Progressivo. Interface em Java (implements) - O que é, para que serve e como
implementar.

No texto abaixo serão passadas informações a respeito de uma interface de Java: a implements.
Clique abaixo para saber mais.

78
Conteúdo interativo disponível na plataforma de ensino!

Linha de Código. Utilizando interfaces.

Clique abaixo para saber mais a respeito de interfaces em Java.

Conteúdo interativo disponível na plataforma de ensino!

79
Classes abstratas e interfaces

APRESENTAÇÃO

As classes abstratas e as interfaces são responsáveis por fazer abstrações, em outras palavras,
elas realizam implementação interna de recursos e mostram apenas a funcionalidade aos
usuários. Ambas trabalham com o conceito de abstração, no entanto, elas têm suas diferenças.
Em Java, a principal diferença entre elas é que os métodos de uma interface Java são
implicitamente abstratos e não podem ter implementações; já uma classe abstrata Java pode ter
métodos de instância que implementam um comportamento padrão.

As classes abstratas podem ter métodos abstratos e não abstratos, diferentemente da interface
que contém apenas métodos abstratos.
Os métodos abstratos são métodos com assinatura válida, no entanto, eles devem ser
sobrescritos e implementados na classe concreta que estende a classe abstrata.

Nesta Unidade de aprendizagem, você aprenderá as definições de classes e de métodos


abstratos em orientação a objetos, analisar
o uso de interfaces, além de aprender como criar uma aplicação utilizando interfaces e classes
abstratas.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Identificar o que são classe abstrata e método abstrato.


• Analisar casos de utilização de herança e de implementação de interface.
• Construir uma aplicação usando classes abstratas e interfaces.

DESAFIO

Sistemas computacionais que exigem reaproveitamento de código, geralmente, são projetados


para facilitar a sua manutenção. Para isso,
é utilizado, com muita frequência, o conceito de herança e polimorfismo. Atrelados ao uso

80
desses conceitos, podem ser utilizadas classes genéricas que nunca serão instanciadas
(abstratas), ou até mesmo
a implementação de métodos alheios a contratos (interface).

Você, como programador de linguagem Java, é convidado para desenvolver um sistema para
uma banca de jornal, cujo proprietário deseja expandir seus negócios para o meio digital. Ele
quer algo simples, uma vez que o negócio recém foi estabelecido. A banca tem apenas livros
(que pode ser classe abstrata) e revistas, os quais implementam
o produto dessa banca. O produto será adicionado como interface para que livros e revistam
tenham, futuramente, um sistema de desconto.
Os livros por sua vez, podem ser ampliados para livro físico, e-book
e minilivro.

Assim, escreva o código de implementação para esse novo sistema.

Para ajudar você em sua programação e guiá-lo no seu passo inicial,


veja a modelagem em UML.

INFOGRÁFICO

81
Uma classe abstrata permite criar funcionalidades que as subclasses podem implementar ou
substituir, enquanto uma interface permite apenas indicar a funcionalidade, mas não
implementá-la. Uma classe pode estender apenas uma classe abstrata, enquanto uma classe pode
implementar várias interfaces.

No Infográfico a seguir, conheça as principais diferenças entre classes abstratas e interfaces.

82
83
CONTEÚDO DO LIVRO

Em se tratando de reaproveitamento de código e de delegação de ações, métodos abstratos e


interfaces podem parecer semelhantes em seus comportamentos, contudo, cada um carrega
consigo sua particularidade, considerando sempre o contexto e sua aplicabilidade.

Na obra Programação orientada a objetos, base teórica desta Unidade de Aprendizagem, leia o
capítulo Classes abstratas e interfaces, no qual você irá ver conceitos e aplicações dessas duas
características enraizadas na linguagem de programação Java, podendo, ao final, diferenciar
uma da outra.

Boa leitura.

84
Classes abstratas
e interfaces
Objetivos de aprendizagem
Ao final deste texto, você deve apresentar os seguintes aprendizados:

 Identificar o que é classe abstrata e método abstrato.


 Analisar casos de utilização de herança e de implementação de
interface.
 Construir uma aplicação usando classes abstratas e interfaces.

Introdução
Dentro do paradigma de programação orientada a objetos, classes abs-
tratas e interfaces são dois conceitos importantes, pois são responsáveis
por determinar comportamentos. Apesar de ambas trabalharem com o
conceito de abstração, elas possuem suas particularidades. A principal
diferença entre elas é que os métodos de uma interface Java são impli-
citamente abstratos, não podendo conter implementação de métodos
em sua classe. Já uma classe abstrata em Java pode ter métodos com sua
codificação, que implementam um comportamento padrão.
Além dos conceitos de classes abstratas e interfaces, existe o con-
ceito de métodos abstratos, que podem ser utilizados em interfaces e
em classes abstratas, uma vez que um método abstrato possui apenas
a assinatura e sua implementação é realizada apenas nas classes que
implementem a classe abstrata ou a interface. No entanto, as classes
abstratas podem ter tanto os métodos abstratos quanto os não abstratos,
diferentemente da interface, que pode possuir apenas métodos abstratos.
Neste capítulo, você estudará sobre os conceitos de classes e mé-
todos abstratos. Além disso, verá quando utilizar os conceitos de he-
rança ou interface. Por fim, verá um exemplo de aplicação prática da
implantação de classes abstratas e interfaces, utilizando a linguagem
de programação Java.

85
Classes abstratas e interfaces

1 Classes e métodos abstratos


Na programação orientada a objetos, existe o conceito de classes, que ge-
ralmente têm referência a alguma abstração do mundo real, como pessoas,
automóveis, animais. Por meio desse paradigma, é possível criar esse tipo de
representação. Em relação à herança, existem as superclasses e as subclasses,
bem como dois outros modos de classificação, denominados classes abstratas
e classes concretas (CARVALHO, 2016).
Uma classe concreta é uma classe comum que pode ser instanciada, ao
contrário de uma classe abstrata, que não pode ser instanciada, devendo
ser estendida. Em geral, uma classe abstrata pode conter métodos abstratos,
ou métodos que não são implementados. Esses métodos possuem assinatura
válida, porém devem ser sobrescritos e implementados na classe concreta que
estende a classe abstrata (FINEGAN; LIGUORI, 2018). As classes abstratas
podem estender outras classes abstratas sem implementar seus métodos, além
de ter variáveis de instância, as quais podem ser usadas pelas classes concretas
que as estendem.
As classes abstratas são criadas para representar, de forma genérica, uma
família de classes, ou seja, elas servem de molde para outras classes. Assim
como as classes, os métodos contidos nelas podem ser concretos — possuem
implementação ou são abstratos — contendo apenas sua declaração, que possui
apenas as definições de sua assinatura.
Por exemplo, considere as formas geométricas triângulo, quadrado e
círculo, que fazem parte de uma família de objetos, a qual pode ser chamada
de FormaGeometrica. Toda forma geométrica pode ser representada
graficamente e pode ter sua área e seu perímetro calculados a partir de suas
medidas. No entanto, cada uma delas possui suas próprias fórmulas para os
cálculos de perímetro e área. Dessa forma, as formas geométricas podem
utilizar o método desenhar, herdado da superclasse FormaGeometrica,
mas devem fornecer soluções próprias para os cálculos de área e perímetro.
A Figura 1, a seguir, apresenta a implementação da classe abstrata For-
maGeometrica, que contém dois métodos abstratos, calcularArea e
calculaPerimetro.

86
Classes abstratas e interfaces

Figura 1. Código Java da classe abstrata FormaGeometrica.


Fonte: Machado, Franco e Bertagnolli. (2016, p. 22).

A Figura 2, a seguir, apresenta a classe concreta Quadrado, que estende


a classe abstrata FormaGeometrica. Os métodos abstratos da classe foram
sobrescritos e implementados na classe Quadrado, e esse mesmo procedi-
mento deve ser realizado para as demais classes que representam outras formas
geométricas, com, por exemplo, um triângulo, ou um círculo entre outras
formas geométricas, pois cada forma geométrica tem sua própria fórmula
para calcular área e perímetro.

Figura 2. Código Java da classe Quadrado, definida como uma especialização de


FormaGeometrica.
Fonte: Machado, Franco e Bertagnolli (2016, p. 22).

87
Classes abstratas e interfaces

Se você declarar um método abstrato, precisará tornar a classe abstrata também, visto
que não é possível ter métodos abstratos em uma classe que não é abstrata. Além disso,
toda classe-filha (subclasse) precisa implementar os métodos abstratos da classe-pai
(superclasse), a não ser que ela também seja abstrata.

2 Utilização de heranças e interfaces


Na orientação a objetos, em algumas situações, é útil definir o que uma classe
deve fazer, porém não se deve determinar como ela o fará. Conforme Schildt
(2014), ao contrário de métodos abstratos, em que método define uma assinatura
com seu comportamento-padrão implementado, as interfaces não definem a
implementação. Em Java, pode-se separar a interface de uma classe de sua
implementação usando a palavra-chave interface.
Uma interface se assemelha a uma classe abstrata, por não possuir nenhum
conteúdo dentro da declaração de suas ações/funções. Logo, uma subclasse
de uma interface deve implementar o corpo, estabelecendo, assim, seus com-
portamentos. Portanto, pode-se chegar à seguinte conclusão: uma interface
especifica o que deve ser feito, mas não como deve ser feito. Após a definição
de uma interface, inúmeras classes podem implementá-la, e uma classe pode
implementar qualquer número de interfaces.

Usando herança e implementação de interfaces


No caso de utilização de classes por meio de herança, pode-se definir uma
classe de maneira especializada. Logo, as subclasses herdam os atributos e
as operações das superclasses. Assim, conforme apresentado na Figura 3, a
classe Carnivoro possuirá o atributo peso e as operações comer e reproduzir,
além daqueles que são específicos da própria subclasse.

88
Classes abstratas e interfaces

Figura 3. Hierarquia de classes.


Fonte: Machado, Franco e Bertagnolli (2016, p. 18).

Utilizando herança, existem casos em que uma classe-filha precisa utilizar


algum método da classe-mãe, porém com algumas características distintas.
Nesse caso, será preciso sobrecarregar o método. O exemplo da Figura 4 aborda
exatamente esse conceito. Observe que, para esse exemplo, existe uma chamada
na classe LampadaNatal para a função acender. Mas por que há essa chamada,
se na classe-mãe Lampada já existe uma função com o mesmo nome? Para esse
caso, é realizada outra ação para o mesmo comportamento de acender, porém
com dois argumentos, indicando os tempos de pisca e a duração da iluminação.

89
Classes abstratas e interfaces

Figura 4. Sobrecarga de métodos em herança.


Fonte: Adaptada de Machado, Franco e Bertagnolli (2016).

A Figura 5, a seguir, apresenta todas as chamadas da classe. Observe que


são feitas duas chamadas à função acender(). A primeira chamada refere-
-se à ação realizada na classe superior, que contém apenas duas ações em seu
corpo — ativa o atributo TestaLigada e chama a função iluminar(),
por não conter nenhum argumento. Já na segunda chamada, são passados
como argumento dois valores, e a função acender()está localizada na
subclasse LampadaNatal.

Figura 5. Exemplo de uso dos métodos herdados e do método sobreposto.


Fonte: Machado, Franco e Bertagnolli (2016, p. 19).

Um dos inconvenientes em se utilizar classes como herança em Java, mesmo


que a classe superior seja concreta ou abstrata, é a não permissão de heranças
90
Classes abstratas e interfaces

múltiplas. Ou seja, uma classe Veículo nunca poderá ser uma moto ou um
carro ao mesmo tempo.
Uma maneira de contornar essa situação é por meio do recurso de interface.
Segundo Machado, Franco e Bertagnolli (2016), interface é a definição de um
contrato, ou seja, seus métodos devem, necessariamente, ser implementados. Uma
vez que uma classe implemente essa interface, essa implementação é realizada
por meio da sintaxe implements em Java. Apenas as assinaturas dos métodos
estão disponíveis, deixando para a classe que o implemento deve prover o com-
portamento ou ação necessária. Por padrão, todos os métodos devem ser públicos
e abstratos — logo, em sua declaração, nenhuma implementação é permitida.
Uma boa maneira de analisar o funcionamento de uma interface, além do
método da prática em si, é por meio da linguagem de modelagem unificada
(UML). No exemplo da Figura 6, a interface Forca representa uma família
de classes (tanto Motor quanto Turbina estão associadas por uma agre-
gação a um tipo de Forca) e permite que o veículo tenha o seu núcleo de
força alterado (como potência, empuxo, nível de ruído) sem a necessidade de
alterações na classe Carro.

Figura 6. Representação do uso de interfaces por diagramação de classes.


Fonte: Adaptado de Machado, Franco e Bertagnolli (2016).

Levando em consideração que existe uma implementação da interface


Forca pelas classes de Motor e Turbina, e não uma extensão, isso permite
91
Classes abstratas e interfaces

que outras interfaces sejam adicionadas ao sistema, como, por exemplo, a


interface Direcao, que pode ser implementada para classes como Manual,
SemiAutomática, Automatica, ou até mesmo, em um futuro não tão
distante, Autonoma, em que cada tipo de direção possui uma forma de ope-
ração diferente. Ainda com base nesse exemplo, a implementação da UML
da Figura 6 pode ser acompanhada na Figura 7.

Figura 7. Codificação da UML apresentada.


Fonte: Adaptado de Machado, Franco e Bertagnolli (2016).

Observe que o carro aceita como novo núcleo qualquer classe que realize a
implementação da interface Forca. Assim, se, no futuro, os carros forem movidos
por um novo tipo de moto, como um reator, basta criar: public class Reator
implements Forca.

Embora as interfaces sejam utilizadas para criar situações de herança múltipla, essa
não é a sua finalidade. Essa função é dada a classes abstratas, que conseguem criar
uma hierarquia de classes e de subtipos. Contudo, não é considerado um erro utilizar
interfaces para simular uma herança.

92
Classes abstratas e interfaces

3 Aplicação com classes abstratas e interfaces


Para o exemplo dado anteriormente, ilustrado pela UML da Figura 6, acrescenta-
remos novos tipos de transportes, como Carro e Motocicleta, com o intuito
de apresentar o uso das características de classes abstratas aliadas a interfaces.
Para tanto, adicionaremos a esse contexto uma classe genérica para representar
esses meios de transportes, denominada Veículo, que será responsável por
comportamentos comuns, como determinar a forca, ligar e desligar.
Na Figura 8, a seguir, é possível acompanhar a implementação da classe-
-mãe. Observe que, embora a classe esteja em modo abstrato, é possível de-
terminar o seu comportamento sem fazer com que ela se estenda ou tenha
que, necessariamente, assinar um contrato, como é feito nas implementações
de interface. Além disso, atributos comuns, como ano e placa, assim como
métodos comuns de ligar e desligar, ficarão por conta dessa classe genérica.

Figura 8. Classe abstrata Veículo.

93
Classes abstratas e interfaces

Com a classe Veículo criada, é possível incorporar suas subclasses ao


programa. Nesse caso, criaremos uma classe chamada Carro, que determinará
a marca e o modelo da instância da classe (Figura 9).

Figura 9. Subclasse Carro e Teste.

Para acompanhar o desempenho das classes adicionadas a essa aplicação,


a Figura 10 apresenta o teste de execução e o seu resultado.

Figura 10. Teste de execução e resultado.

94
Classes abstratas e interfaces

Nessa pequena aplicação, criamos apenas uma subclasse de Veículo,


denominada de Carro. No entanto, nada impede que sejam acrescentados
novos tipo de veículo à estrutura. Para Turbina, acrescentaremos um tipo
de campo enum, chamado de TipoTurbina, para facilitar a chamada a tipos
variados de turbina nesse exemplo hipotético, os quais são declarados de forma
estática. A Figura 11, a seguir, apresenta essa implementação.

Figura 11. Subclasse Carro e Teste.

CARVALHO, T. L. Orientação a objetos: aprenda seus conceitos e suas aplicabilidades


de forma efetiva. São Paulo: Casa do Código, 2016.
FINEGAN, E.; LIGUORI, R. OCA Java SE 8: guia de estudos para o exame 1Z0-808. 3. ed.
Porto Alegre: Bookman, 2018.
MACHADO, R. P.; FRANCO, M. H. I.; BERTAGNOLLI, S. de C. Desenvolvimento de software
III: programação de sistemas web orientada a objetos em Java. Porto Alegre: Book-
man, 2016.
SCHILDT, H. Java para iniciantes. 6. ed. Porto Alegre: Bookman, 2014.

95
DICA DO PROFESSOR

Abstract factory é um padrão de design que fornece uma interface


para criar famílias de objetos relacionados ou dependentes sem especificar suas classes
concretas. Uma fábrica abstrata é uma
classe que fornece uma interface para produzir uma família de objetos. Em Java, esse
padrão pode ser implementado usando-se
uma interface ou uma classe abstrata.

Nesta Dica do Professor, você vai entender o abstract factory.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Normalmente, em programação orientada a objetos, utiliza-se o conceito de que uma


classe pode receber atributos e métodos de uma classe denominada superclasse. Qual
o nome dessa característica?

A) Característica polimórfica.

B) Característica de uma interface.

C) Característica de herança.

D) Característica de uma implementação.

E) Característica de uma abstração.

96
2) Normalmente, uma classe declarada como genérica, não tem um objeto com sua
instância e, por conta disso, algumas situações exigem que sua declaração seja
abstrata. Avalie as seguintes sentenças e marque a alternativa correta.

A) Toda classe abstrata servirá apenas de modelo. Logo, seus métodos deverão ser
sobrescritos, mesmo que tenham sua implementação.

B) Uma classe abstrata servirá de modelo para outras classes. Seus métodos devem ser
concretos, exigindo sua implementação, contendo, assim, as definições de sua assinatura.

C) Uma classe abstrata servirá de modelo para outras classes. Seus métodos devem ser
abstratos, ma são implementados na classe abstrata e têm definições de sua assinatura.

D) Uma classe abstrata servirá de modelo para outras classes. Seus métodos podem ser
abstratos e concretos, no entanto, eles não são implementados na classe abstrata, mas têm
definições de sua assinatura.

E) Toda classe abstrata servirá apenas de modelo. Logo, seus métodos deverão ser
sobrescritos quando declarados como abstract, mesmo que tenham sua implementação.

3) Há situações em que não é desejável que certas classes tenham os mesmos privilégios
que outras, como em um sistema bancário. Embora o cliente e o gerente herdem um
método para login, os dois não estão no mesmo nível hierárquico, mesmo que ambos
herdem a mesma característica de uma superclasse denominada pessoa. Para
contornar essa situação e fazer com que cada um tenha sua própria tela de login,
assinale a alternativa que mostra o melhor caminho.

A) Para que o usuário (cliente) faça login, de forma diferente do gerente e de outros tipos de
usuários, é possível estender uma interface chamada login e implementar sua própria
função de acesso, sem sobrescrever qualquer método.

B) Para que o usuário (cliente) faça login, de forma diferente do gerente e de outros tipos de

97
usuários, ele deve estender uma classe abstrata, herdando assim sua implementação de
acesso.

C) Para que o usuário (cliente) faça login, de forma diferente do gerente e de outros tipos de
usuários, é possível implementar uma interface chamada login e programar sua própria
função de acesso, sem sobrescrever qualquer método.

D) Para que o usuário (cliente) faça login, de forma diferente do gerente e de outros tipos de
usuários, é possível implementar uma interface chamada login para incorporar a
programação do método de acesso da interface, tornando-a independente da forma de
implementação de sua superclasse.

E) Para que o usuário (cliente) faça login, de forma diferente do gerente e de outros tipos de
usuários, é possível programar uma interface chamada login para implementar sua própria
forma de acesso, tornando-a independente da forma de implementação de sua superclasse.

4) Um método abstrato tem apenas a assinatura e sua implementação e é realizado


apenas em classes que implementam a classe abstrata ou a interface. Considerando as
propriedades das classes abstratas, qual a saída do seguinte programa?

Conteúdo interativo disponível na plataforma de ensino!

A) Erro de compilação.

B) 2.

C) 4.

D) 10.

E) 20.

98
5) Mesmo que as classes abstratas sejam parecidas com as interfaces, ambas têm
particularidades que as tornam únicas. Logo, marque a resposta que melhor
representa cada uma delas.

A) Interfaces podem ter apenas métodos abstratos públicos; as classes abstratas não podem
conter campos de dados.

B) Na classe abstrata, a palavra-chave abstract é opcional para declarar um método. Enquanto


na interface, a palavra-chave abstract é obrigatória para declarar um método como um
resumo.

C) Tanto as interfaces como as classes abstratas podem conter métodos concretos.

D) Interface é utilizada quando implementações compartilham apenas a assinatura do método;


classe abstrata é usada quando várias implementações do mesmo tipo compartilham um
comportamento comum.

E) Interface é utilizada quando várias implementações do mesmo tipo compartilham um


comportamento comum; classe abstrata é usada quando implementações compartilham
apenas a assinatura do método.

NA PRÁTICA

O conceito de abstração tem papel fundamental para tratar


de generalizações de classes em relação aos métodos.
Existem os conceitos de classes abstratas e de interfaces
que são os responsáveis por realizar abstrações de dados.

Neste Na Prática, veja um exemplo de aplicação dos conceitos


de interface e de classes abstratas no desenvolvimento de um
editor de cena.

99
100
SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Android Interface Definition Language (AIDL)

O método final não pode ser substituído, assim, uma função abstrata não pode ser final. O
sistema Android Interface Definition Language permite definir a interface de programação que o
cliente e o serviço usam para se comunicar entre si, a partir da comunicação entre processos
(IPC). No Android, um processo normalmente não pode acessar a memória de outro.

Conteúdo interativo disponível na plataforma de ensino!

Qual a diferença entre classe interna, classe aninhada e classe anônima?

Conteúdo interativo disponível na plataforma de ensino!

Java Interface: Aprenda a usar corretamente

Conteúdo interativo disponível na plataforma de ensino!

101
Atributos e métodos de classe

APRESENTAÇÃO

Atributos e métodos são elementos importantes no desenvolvimento de aplicações. Sabemos que


os atributos têm a capacidade de guardar valores, os quais são os dados, que podem ser inseridos
por um usuário ou inicializados dentro da classe. Métodos são ações ou sub-rotinasI que operam
sobre os dados da classe, muitas vezes servindo para acessar esses dados.

Em orientação a objetos, possuímos dois tipos de atributos, de instância e de classe ou estático, e


a mesma regra segue para métodos. Com isso, temos métodos de instância e de classe ou
estático.

Nesta Unidade de Aprenduizagem veremos qual a diferença entre os tipos, para que possamos
fazer bom uso destes elementos em programação orientada a objetos.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Identificar atributos de instância e de classe ou estáticos.


• Definir métodos de instância e de classes ou estáticos.
• Construir classes com métodos e atributos de classes ou estáticos.

DESAFIO

Trabalhar com métodos e atributos estáticos pode nos trazer muitas vantagens, como economia
de código. Entretanto, é importante termos um bom conhecimento sobre o assunto para
aplicarmos em nossos projetos.

Você trabalha como analista/programador em uma fábrica de software e foi encarregado de


atender um cliente de uma fábrica. O cliente necessita saber se a quantidade que produz
mensalmente, somada ao estoque inicial, pode atender à demanda do mês. Entretanto, a
aplicação não está rodando. Com análises preliminares, você descobriu que
o problema está na classe “Producao”.
102
Verifique o código da classe e corrija o problema.

O código da classe “Producao” atual está assim:

INFOGRÁFICO

No infográfico a seguir, veja as características de atributos e métodos estáticos ou de classe.

103
CONTEÚDO DO LIVRO

Membros estáticos ou de classe podem nos trazer uma redução significativa de código e espaço
em memória, sem contar com as vantagens funcionais. Portanto, o domínio destes conceitos
torna-se vital para o desenvolvimento de softwares.

Acompanhe um trecho do livro Conceitos de computação em Java, livro que serve de base
teórica para esta Unidade de Aprendizagem. Inicie o estudo pelo tópico 8.6 Métodos estáticos e
finalize ao final de 8.7 Campos estáticos.

Boa leitura.

104
CAY HORSTMANN

CONCEITOS DE
C O M P U TA Ç Ã O C O M
Compatível com
Java 5 & 6

Java
5a Edição

105
CAPÍTULO 8 䊏 Projetando Classes

8.6 Métodos estáticos


Um método estático não é Às vezes você precisa de um método que não seja invocado a partir
invocado em um objeto. de um objeto. Chamamos esse método de método estático ou de
método de classe. Por outro lado, os métodos que você escreveu
até agora são freqüentemente chamados de métodos de instância porque operam em uma
instância particular de um objeto.

106
Conceitos de Computação com Java

Um exemplo típico de um método estático é o método sqrt da classe Math. Quando


você chama Math.sqrt(x), você não fornece nenhum parâmetro implícito. (Lembre-se de
que Math é o nome de uma classe, não de um objeto.)
Por que você iria querer escrever um método que não opera em um objeto? A razão
mais comum é encapsular algum cálculo que envolve apenas números. Como números
não são objetos, você não pode invocar métodos neles. Por exemplo, a chamada x.sqrt()
nunca será válida em Java.
Eis um exemplo típico de um método estático que realiza alguns cálculos algébricos
simples: calcular a porcentagem p da quantia a. Como os parâmetros são números, o mé-
todo não opera em absolutamente nenhum objeto, portanto nós o tornamos um método
estático:
/**
Calcula uma porcentagem de uma quantia.
@param p porcentagem a aplicar
@param a quantia à qual a porcentagem é aplicada
@return p porcentagem de a
*/
public static double percentOf(double p, double a)
{
return (p / 100) * a;
}

Você precisa encontrar um local para esse método. Vamos pensar em uma nova classe
(semelhante à classe Math da biblioteca Java padrão). Como o método percentOf tem a
ver com cálculos financeiros, projetaremos uma classe Financial para armazená-lo. Eis
a classe:
public class Financial
{
public static double percentOf(double p, double a)
{
return (p / 100) * a;
}
// Outros métodos financeiros podem ser adicionados aqui.
}

Ao chamar um método estático, você fornece o nome da classe que contém o método
para que o compilador possa localizá-lo. Por exemplo,
double tax = Financial.percentOf(taxRate, total);

Observe que você não fornece um objeto do tipo Financial ao chamar o método.
Agora podemos dizer por que o método main é estático. Quando o programa inicia,
não há nenhum objeto. Portanto, o primeiro método no programa deve ser um método
estático.
Talvez você esteja se perguntando por que esses métodos são chamados estáticos.
O significado normal da palavra estático (“permanecer fixo em um lugar”) não parece
estar relacionado com aquilo que os métodos estáticos fazem. Na realidade, essa palavra
foi adotada por acidente. Java usa a palavra-chave static porque C++ a usa no mesmo
contexto. C++ usa static para indicar métodos de classe porque os criadores de C++ não
queriam criar uma outra palavra-chave. Alguém observou que havia uma palavra-chave

107
CAPÍTULO 8 䊏 Projetando Classes

raramente utilizada, static, que indica algumas variáveis que permanecem em uma loca-
lização fixa para múltiplas chamadas de método. (Java não tem esse recurso, nem precisa
dele.) Acabou-se descobrindo que a palavra-chave poderia ser reutilizada para indicar
métodos de classe sem confundir o compilador. O fato de que ela pode confundir as
pessoas aparentemente não foi uma grande preocupação. Você simplesmente tem de con-
viver com o fato de que “método estático” significa “método de classe”: um método que
não opera em um objeto e que só tem parâmetros explícitos.

AUTOVERIFICAÇÃO DA APRENDIZAGEM
12. Suponha que Java não tivesse métodos estáticos. Todos os métodos da classe
Math seriam então métodos de instância. Como você calcularia a raiz quadrada
de x?
13. Harry entrega seu dever de casa, um programa que executa o jogo-da-velha. A
solução dele consiste em uma única classe com muitos métodos estáticos. Por
que isso não é uma solução orientada a objetos?

8.7 Campos estáticos


Às vezes, você precisa armazenar valores fora de um objeto específico. Utilize campos
estáticos para esse propósito. Eis um exemplo típico. Utilizaremos uma versão da nossa
classe BankAccount em que cada objeto conta bancária tem um saldo e um número de
conta:
public class BankAccount
{
. . .
private double balance;
private int accountNumber;
}

Queremos atribuir números de conta seqüencialmente. Isto é, queremos que o construtor


de conta bancária crie a primeira conta com o número 1001, a próxima com o número
1002 e assim por diante. Portanto, devemos armazenar o último número de conta atri-
buído em algum lugar.
Não faz sentido, porém, transformar esse valor em um campo de instância:
public class BankAccount
{
. . .
private double balance;
private int accountNumber;
private int lastAssignedNumber = 1000; // NÃO – não funcionará
}

Nesse caso, cada instância da classe BankAccount teria um valor próprio de lastAssigned-
Number.

108
Conceitos de Computação com Java

Um campo estático Em vez disso, precisamos ter um único valor de lastAssigned-


pertence à classe, não a um Number que seja o mesmo para toda a classe. Esse campo é chamado
objeto da classe. campo estático, porque você o declara utilizando a palavra-chave
static.
public class BankAccount
{
. . .
private double balance;
private int accountNumber;
private static int lastAssignedNumber = 1000;
}

Cada objeto BankAccount tem campos de instância balance e accountNumber próprios, mas
há apenas uma única cópia da variável lastAssignedNumber (veja Figura 4). Esse campo é
armazenado em um local separado, fora de qualquer objeto BankAccount.
Um campo estático às vezes é chamado campo de classe porque há um único campo
para toda a classe.
Cada método de uma classe pode acessar seus campos estáticos. Eis o construtor da
classe BankAccount, que incrementa o último número atribuído e então o usa para inicia-
lizar o número de conta do objeto a ser construído:
public class BankAccount
{
public BankAccount()
{
// Gera o próximo número de conta a ser atribuído
lastAssignedNumber++; // Atualiza o campo estático
// Atribui o campo ao número de conta dessa conta bancária
accountNumber = lastAssignedNumber; // Configura o campo de instância
}
. . .
}

Como você inicializa um campo estático? Você não pode configurá-lo no construtor da
classe:
public BankAccount()
{
lastAssignedNumber = 1000; // NÃO – seria redefinido para 1000 a cada novo objeto
. . .
}

Assim, a inicialização ocorreria toda vez que uma nova instância fosse construída.
Há três maneiras de inicializar um campo estático:
1. Não fazer nada. O campo estático é então inicializado com 0 (para números),
false (para valores boolean) ou null (para objetos).
2. Utilizar um inicializador explícito, como:
public class BankAccount
{
. . .
private static int lastAssignedNumber = 1000;
}

109
CAPÍTULO 8 䊏 Projetando Classes

Cada objeto
collegeFund = BankAccount
BankAccount tem um campo
accountNumber
balance = 10000 próprio
accountNumber = 1001

momsSavings =
BankAccount

balance = 8000
accountNumber = 1002

harrysChecking =
BankAccount

balance = 0
accountNumber = 1003
Há um único campo
lastAssignedNumber
para a classe
BankAccount

BankAccount.lastAssignedNumber = 1003

Figura 4 Um campo estático e campos de instância.

A inicialização é executada depois que a classe é carregada.


3. Utilizar um bloco de inicialização estático (ver Tópico Avançado 8.3).
Como ocorre com campos de instância, campos estáticos sempre devem ser declarados
como private para assegurar que os métodos das outras classes não alterem seus valores.
A exceção a essa regra são as constantes estáticas, que podem ser privadas ou públicas.
Por exemplo, a classe BankAccount poderia definir o valor de uma constante pública,
como
public class BankAccount
{
. . .
public static final double OVERDRAFT_FEE = 5;
}

Métodos de qualquer classe referenciam essa constante como BankAccount.OVERDRAFT_FEE.


Faz sentido declarar constantes como static – você não iria querer que cada objeto
da classe BankAccount tivesse seu próprio conjunto de variáveis com os valores dessas
constantes. É suficiente ter um conjunto delas para a classe.
Por que as variáveis de classe são chamadas static? Como ocorre com os métodos
estáticos, a própria palavra-chave static é simplesmente uma remanescente sem sentido

110
Conceitos de Computação com Java

de C++. Mas campos estáticos e métodos estáticos têm muito em comum: eles são apli-
cados a toda a classe, não a instâncias específicas da classe.
Em geral, é recomendável minimizar o uso dos campos e métodos estáticos. Se en-
contrar utilizando vários métodos estáticos é uma indicação de que talvez você não tenha
encontrado as classes corretas para resolver seu problema de uma maneira orientada a
objetos.

AUTOVERIFICAÇÃO DA APRENDIZAGEM
14. Cite dois campos estáticos da classe System.
15. Harry informa que encontrou uma excelente maneira de evitar esses objetos in-
cômodos: colocar todo o código em uma única classe e declarar todos os méto-
dos e campos como static. Então main pode chamar os outros métodos estáticos
e todos eles podem acessar os campos estáticos. O plano do Harry funcionará?
Ele é uma boa idéia?

TÓPICO AVANÇADO 8.3

Formas alternativas de inicialização de campos


O Tópico Avançado 8.3 abrange dois mecanismos menos comuns para inicialização de
campo: especificar os valores iniciais para os campos e usar blocos de inicialização.

111
DICA DO PROFESSOR

Assista ao vídeo e veja na prática o uso de atributos de classe.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Marque a alternativa correta.

A) Uma variável estática representa informações em nível de classe.

B) Uma variável estática não muda seu valor.

C) Usamos variáveis de classe quando apenas um objeto da classe precisa utilizar uma cópia
desta variável.

D) Todo objeto tem sua própria cópia de todas as variáveis estáticas da classe.

E) Se vários objetos precisam acessar uma determinada variável em comum, a medida que
transformamos esta variável em estática, estamos desperdiçando espaço na memória.

2) A declaração de uma variável de classe começa com qual


palavra-chave ?

A) public.

B) class.

C) void.

112
D) private.

E) static.

3) Em relação aos métodos estáticos ou de classes, marque a afirmativa correta.

A) Métodos são variáveis que tem a capacidade de receber uma quantidade maior de
informação.

B) Métodos estáticos não são membros de classes.

C) Métodos estáticos servem apenas para operações de inserção de dados.

D) Por serem estáticos, os métodos não podem ser chamados em outras classes.

E) Métodos estáticos são declarados colocando-se a palavra-chave static antes do tipo de


retorno.

4) Para um método de classe acessar membros de classe não estáticos, devemos:

A) Colocar a palavra-chave static no membro a ser acessado.

B) Colocarmos a palavra-chave protected no membro a ser acessado.

C) Um método estático não pode acessar membros de classe não estáticos.

D) Devemos declarar o método usando o get na frente do seu nome.

E) Devemos declarar o método usando o set na frente do seu nome.

113
5) Marque a afirmativa correta.

A) As variáveis e os métodos de classe estáticos existem apenas quando um objeto dessa


classe tenha sido instanciado.

B) A referência this pode ser usada em métodos estáticos.

C) Se um método estático tentar acessar um outro método não estático da classe usando
somente o nome do método, ocorrerá um erro de compilação.

D) Para fazermos a chamada de um método estático, é necessário apenas colocar o nome do


método seguido de parênteses.

E) Métodos e variáveis estáticas são associados a um objeto.

NA PRÁTICA

Cada objeto possui sua cópia de todos os atributos de instância de uma classe, entretanto, em
alguns casos, é interessante que haja um atributo que possa ser compartilhado entre todos os
objetos de uma classe, e a ele damos o nome de atributo de classe ou estático. A maioria dos
métodos é executada dependendo da chamada de um objeto específico, todavia, existem
métodos que não dependem de nenhum objeto; a eles damos o nome de métodos de classe ou
estáticos.

114
SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Variáveis e métodos estáticos

Veja o vídeo onde é apresentado mais sobre Variáveis e Métodos Estáticos.

Conteúdo interativo disponível na plataforma de ensino!

Classificação automática de textos utilizando aprendizado supervisionado baseado em


uma unica classe

Nesse artigo você verá sobre os métodos de aprendizado de máquina supervisionados baseados
em uma unica classe para classificação automática.

Conteúdo interativo disponível na plataforma de ensino!

115
Atributos e métodos finais

APRESENTAÇÃO

Atributos e métodos finais são utilizados para garantir que determinada implementação não
tenha modificações, isto é, garantir a imutabilidade.

Nesta Unidade de Aprendizagem conheceremos um pouco mais sobre atributos e métodos


finais, assim como sua aplicação.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Reconhecer método final.


• Definir atributo final.
• Construir uma aplicação utilizando atributos e métodos finais.

DESAFIO

Determinar o preço de venda de uma mercadoria ou serviço é muito importante para qualquer
comerciante ou empresário, pois erros de cálculo podem causar um enorme prejuízo. Portanto, é
necessário termos um índice, que, quando multiplicado pelo valor de compra, traga o preço final
da mercadoria. O markup é exatamente isso, um índice.

116
Crie a aplicação com uma variável final e, depois, um método final para calcular o preço de
venda do produto. A formula para tal é (markup*preço*compra), crie uma classe controle para
instanciar a classe que contenha a variável e o método, e coloque alguns valores para testar a
aplicação. Utilize uma linguagem de programação orientada a objetos para criar a aplicação,
depois exporte o projeto em formato zip.

Como sugestão para facilitar o trabalho, utilize o IDE netbeans ou um de sua preferência.

INFOGRÁFICO

Veja no infográfico os conceitos de palavra-chave, atributos e métodos finais.

117
CONTEÚDO DO LIVRO

Qualquer que seja a razão, em Java, com o uso da palavra-chave final, é fácil impedir que um
método seja sobreposto ou uma classe seja herdada.

Acompanhe um trecho do livro Java para iniciantes, que traz uma abordagem sobre o uso da
palavra-chave final. O livro serve de base teórica para esta Unidade de Aprendizagem. Inicie o
estudo pelo tópico Usando final e finalize em Pergunte ao especialista.

Boa leitura.

118
Atualizado para JAVA SE 8 (JDK 8)

Java
para iniciantes

Crie, compile e execute programas Java rapidamente

Herbert Schildt
119
O autor
O autor de best-sellers Herbert Schildt escreve incansavelmente sobre programação
há quase três décadas e é uma das principais autoridades na linguagem Java. Seus
livros de programação venderam milhões de cópias no mundo inteiro e foram tra-
duzidos para diversos idiomas. É autor de vários livros sobre Java, incluindo Java:
The Complete Reference, Herb Schildt’s Java Programming Cookbook e Swing: A
Beginner’s Guide. Ele também escreveu sobre C, C++ e C#. Embora tenha interesse
em todas as áreas da computação, seu foco principal são as linguagens de progra-
mação, incluindo compiladores, interpretadores e linguagens de controle robótico.
Também tem grande interesse na padronização de linguagens. Schildt tem gradua-
ção e pós-graduação pela Universidade de Illinois. Seu site é www.HerbSchildt.com.

O editor técnico
Dr. Danny Coward trabalhou em todas as edições da plataforma Java. Ele conduziu
a definição dos Java Servlets para a primeira versão da plataforma Java EE e para
além dela, os serviços web para a plataforma Java ME, e a estratégia e planejamento
de Java SE 7. Fundou a tecnologia JavaFX e, mais recentemente, projetou o maior
acréscimo feito ao padrão Java EE 7, a API Java WebSocket. Da codificação em Java
ao projeto de APIs com especialistas da indústria e ao trabalho por vários anos como
executivo do Java Community Process, ele adquiriu uma perspectiva singularmente
ampla de vários aspectos da tecnologia Java. Além disso, é autor de JavaWebSo-
cket Programming e de um livro ainda a ser publicado sobre Java EE. Dr. Coward
tem graduação, mestrado e doutorado em Matemática pela Universidade de Oxford.

S334j Schildt, Herbert.


Java para iniciantes : crie, compile e execute programas
Java rapidamente [recurso eletrônico] / Herbert Schildt ;
tradução: Aldir José Coelho Corrêa da Silva ; revisão
técnica: Maria Lúcia Blanck Lisbôa. – 6. ed. – Porto Alegre :
Bookman, 2015.

Editado como livro impresso em 2015.


ISBN 978-85-8260-337-6

1. Linguagem de programação - Java. I. Título.

CDU 004.438Java

Catalogação na publicação: Poliana Sanchez de Araujo – CRB 10/2094

120
Java para Iniciantes

Usando final
Mesmo com a sobreposição de métodos e a herança sendo tão poderosas e úteis,
podemos querer evitar que ocorram. Por exemplo, podemos ter uma classe que en-
capsule o controle de algum dispositivo de hardware. Além disso, essa classe pode
dar ao usuário a oportunidade de inicializar o dispositivo, fazendo uso de informa-
ções privadas. Nesse caso, não vamos querer que os usuários de nossa classe possam
sobrepor o método de inicialização. Qualquer que seja a razão, em Java, com o uso
da palavra-chave final, é fácil impedir que um método seja sobreposto ou uma classe
seja herdada.

A palavra-chave final impede a sobreposição


Para impedir que um método seja sobreposto, especifique final como modificador no
início de sua declaração. Métodos declarados como final não podem ser sobrepostos.
O fragmento a seguir ilustra final:
class A {
final void meth() {
System.out.println("This is a final method.");
}
}

class B extends A {
void meth() { // ERRO! não pode sobrepor.
System.out.println("Illegal!");
}
}

Já que meth( ) é declarado como final, não pode ser sobreposto em B. Se você tentar
fazê-lo, ocorrerá um erro de tempo de compilação.

A palavra-chave final impede a herança


Você pode impedir que uma classe seja herdada precedendo sua declaração com
final. A declaração de uma classe como final também declara implicitamente todos
os seus métodos como final. Como era de se esperar, é inválido declarar uma classe
como abstract e final, uma vez que uma classe abstrata é individualmente incomple-
ta e depende de suas subclasses para fornecer implementações completas.
Aqui está um exemplo de uma classe final:
final class A {
// ...
}

// A classe seguinte é inválida.


class B extends A { // ERRO! não pode criar uma subclasse de A
// ...
}

Como os comentários sugerem, é inválido B herdar A, já que A é declarada como


final.

121
Capítulo 7 Herança

Usando final com membros de dados


Além dos usos que acabei de mostrar, final também pode ser aplicada a variáveis mem-
bros para criar o que seriam constantes nomeadas. Se você preceder o nome da variável
de uma classe com final, seu valor não poderá ser alterado durante todo o tempo de vida
do programa. Certamente, você pode dar a essa variável um valor inicial. Por exemplo,
no Capítulo 6, uma classe simples de gerenciamento de erros chamada ErrorMsg foi
mostrada. Essa classe mapeava um string legível por humanos para um código de erro.
Aqui, a versão original da classe será melhorada pelo acréscimo de constantes final,
que representam os erros. Agora, em vez de passar para getErrorMsg( ) um número
como 2, você pode passar a constante int nomeada DISKERR.
// Retorna um objeto String.
class ErrorMsg {
// Códigos de erro.
final int OUTERR = 0;
final int INERR = 1; Declara constantes final.
final int DISKERR = 2;
final int INDEXERR = 3;

String msgs[] = {
"Output Error",
"Input Error",
"Disk Full",
"Index Out-Of-Bounds"
};

// Retorna a mensagem de erro.


String getErrorMsg(int i) {
if(i >=0 & i < msgs.length)
return msgs[i];
else
return "Invalid Error Code";
}
}

class FinalD {
public static void main(String args[]) { Usa constantes final.
ErrorMsg err = new ErrorMsg();

System.out.println(err.getErrorMsg(err.OUTERR));
System.out.println(err.getErrorMsg(err.DISKERR));
}
}

Observe como as constantes final são usadas em main( ). Uma vez que são membros
da classe ErrorMsg, devem ser acessadas via um objeto dessa classe. É claro que
também podem ser herdadas pelas subclasses e acessadas diretamente dentro delas.
Por uma questão estilística, muitos programadores de Java usam identificado-
res maiúsculos em constantes final, como no exemplo anterior, mas essa não é uma
regra fixa.

122
Java para Iniciantes

Pergunte ao especialista
P: Variáveis membros final podem ser transformadas em static? A palavra-chave
final pode ser usada em parâmetros de métodos e variáveis locais?
R: A resposta às duas perguntas é sim. Transformar uma variável membro final em
static permite que você referencie a constante pelo nome de sua classe em vez de
por um objeto. Por exemplo, se as constantes de ErrorMsg fossem modificadas por
static, as instruções println( ) de main( ) teriam esta aparência:
System.out.println(err.getErrorMsg(ErrorMsg.OUTERR));
System.out.println(err.getErrorMsg(ErrorMsg.DISKERR));

A declaração de um parâmetro final impede que ele seja alterado dentro do método. A
declaração de uma variável local final impede que ela receba um valor mais de uma vez.

A classe Object
Java define uma classe especial chamada Object que é uma superclasse implícita de
todas as outras classes. Em outras palavras, todas as outras classes são subclasses de
Object. Ou seja, uma variável de referência de tipo Object pode referenciar um ob-
jeto de qualquer outra classe. Além disso, uma vez que os arrays são implementados
como classes, uma variável de tipo Object também pode referenciar qualquer array.
Object define os métodos a seguir, portanto, eles estão disponíveis em todos
os objetos:

Método Finalidade
Object clone( ) Cria um novo objeto igual ao objeto que está sendo
clonado.
boolean equals(Object objeto) Determina se um objeto é igual a outro.
void finalize() Chamado antes de um objeto não usado ser
reciclado.
Class<?> getClass( ) Obtém a classe de um objeto no tempo de execução.
int hashCode( ) Retorna o código hash associado ao objeto chamador.
void notify( ) Retoma a execução de uma thread que está
esperando no objeto chamador.
void notifyAll( ) Retoma a execução de todas as threads que estão
esperando no objeto chamador.
String toString() Retorna um string que descreve o objeto.
void wait( ) Espera outra thread de execução.
void wait(long milissegundos)
void wait(long milissegundos,
int nanossegundos)

Os métodos getClass( ), notify( ), notifyAll( ) e wait( ) são declarados como final.


Você pode sobrepor os outros. Vários desses métodos serão descritos posteriormente no
livro. No entanto, veremos dois agora: equals( ) e toString( ). O método equals( )

123
DICA DO PROFESSOR

Assista ao vídeo e veja na prática o uso de métodos e atributos finais.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Qual o objetivo de usarmos a palavra-chave final em membros de uma classe?

A) Encapsulamento.

B) Dar visibilidade pública aos membros da classe.

C) Para deixarmos os membros visíveis em sua classe e subclasses.

D) Para deixar o membro imutável.

E) A palavra-chave final é utilizada apenas em classe.

2) Em relação aos atributos de uma classe, podemos afirmar que a palavra-chave final é
usada para:

A) Declarar variáveis.

B) Declarar uma constante.

C) Para declararmos atributos estáticos.

124
D) Para declararmos atributos que receberam valor de ponto flutuante.

E) Para não declararmos dois ou mais atributos com o mesmo nome.

3) Referindo-nos a métodos, o que define o uso da palavra-chave final? Marque a


alternativa correta.

A) Impede que um método seja sobreposto.

B) Impede que o método fique visível para outras classes.

C) Informa que o método não possui retorno.

D) Transforma o método em um membro estático.

E) A palavra-chave final é utilizada apenas em métodos estáticos.

4) Observe os trechos de códigos abaixo e marque a alternativa incorreta.

A) private final int num = 2;

B) public final void imprime(){ System.out.println("Imprime texto"); }

C) public abstract final void som();

D) private final int num1 = 6; private final int num2 = 4; private int result; public final void(){
result = num1 + num2; }

E) static private final double pi = 3.1416; private double raio = 2; private double area; public

125
void setRaio(){ this.raio = raio; area = pi * (raio*raio); }

5) Analise o código e marque a alternativa incorreta.

126
127
A) A classe pessoa possui um método final.

B) Nenhum dos atributos da classe pessoa foram declarados como constantes.

C) A classe homem está herdando os membros da classe pessoa.

D) Se instanciarmos a classe Pessoa , chamarmos o método calculaIdade e passarmos os


valores referentes a ano de nascimento e ano atual por parâmetros no método, teremos
como resultado a idade.

E) Se instanciarmos a classe Homem, chamarmos o método calculaIdade e passarmos os


valores referentes a ano de nascimento e ano atual por parâmetros no método, teremos
como resultado a idade.

NA PRÁTICA

Em relação a atributos, quando queremos garantir a imutabilidade de seu valor, utilizamos a


palavra-chave final em sua declaração. Um exemplo de declaração seria final int X;.

128
Os atributos finais também são conhecidos como constantes. Podemos exemplificar como
aplicação um atributo que guarde o valor do "PI", não é interessante que esse valor mude em sua
aplicação, pois trata-se de algo constante.

Os nomes de atributos finais devem ser escritos com letras maiúsculas; por exemplo, PI,
TAMANHO, VALOR, PERCENTUAL.

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Como declarar e usar Constantes em Java

Conteúdo interativo disponível na plataforma de ensino!

Java Progressivo. Use constantes, e não números - declarando variáveis com o final

Conteúdo interativo disponível na plataforma de ensino!

129
Encapsulamento e modificadores de
acesso

APRESENTAÇÃO

O encapsulamento é um mecanismo de programação que vincula o código e os dados que ele


trata, e isso mantém os dois seguros contra a interferência e a má utilização externa. Em uma
linguagem orientada a objetos, o código e os dados podem ser vinculados de tal forma que uma
caixa preta autônoma seja criada. Dentro da caixa estão todos os códigos e os dados necessários.
Quando o código e os dados são vinculados dessa forma, um objeto é criado. Em outras
palavras, um objeto é o dispositivo que dá suporte ao encapsulamento.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é encapsulamento.


• Reconhecer modificadores e métodos de acesso.
• Construir uma aplicação utilizando técnicas de encapsulamento.

DESAFIO

Trabalhar com encapsulamento em orientação a objetos pode nos trazer muitas vantagens, como
diminuir a complexidade no desenvolvimento da aplicação; entretanto, é importante termos um
bom conhecimento sobre o assunto para aplicarmos da melhor forma em nossos projetos.
Você trabalha como analista/programador em uma fábrica de software e foi encarregado de criar
uma aplicação que calcule o volume de concreto para o trabalho com vigas em construção civil.
Sabe-se que a fórmula para o cálculo deste volume é (base x altura x comprimento).
O cliente necessita de uma aplicação que solicite, via caixa de diálogo, os dados referentes à
base, altura e comprimento das vigas a serem trabalhadas e seja informado o resultado através
de uma caixa de mensagem.

Sua tarefa é implementar esta aplicação em uma linguagem de programação orientada a objetos;

130
faça uso das técnicas de encapsulamento.

INFOGRÁFICO

Veja, no infográfico, os conceitos de encapsulamento, modificadores e métodos de acesso.

CONTEÚDO DO LIVRO

Modificadores de acesso possuem um papel importante na orientação a objetos. Portanto, o


domínio destes conceitos torna-se vital para o desenvolvimento de softwares. Acompanhe o
capítulo Encapsulamento e Modificadores de Acesso da obra Programação orientada a objetos
e aprofunde seus estudos.

Boa leitura.

131
Encapsulamento e
modificadores de acesso
Objetivos de aprendizagem
Ao final deste texto, você deve apresentar os seguintes aprendizados:

„„ Definir encapsulamento.
„„ Reconhecer os modificadores e os métodos de acesso.
„„ Construir uma aplicação utilizando técnicas de encapsulamento.

Introdução
Neste capítulo, você estudará o encapsulamento e os modificadores de
acesso. Além disso, aprenderá como utilizar os modificadores de aces-
sos e quais as diferenças entre eles, tonando-se apto para desenvolver
programas utilizando este aprendizado.

Encapsulamento
Encapsulamento em programação orientada a objetos (POO) significa separar o
programa em partes, deixando-o mais isolado possível. Dessa forma, é possível
torná-lo mais flexível, fácil de modificar e manter, bem como implementar
novas funcionalidades.

132
Encapsulamento e modificadores de acesso

Trata-se de uma forma muito eficiente de proteger dados que são ma-
nipulados dentro da classe, determinando onde ela poderá ser manipulada.
Geralmente, utiliza-se o acesso mais restrito (private) para que não ocorra
acesso público aos membros. O encapsulamento ocorre em dois níveis, con-
forme você pode ver a seguir.

„„ Nível de classe: em que se determina o acesso de uma classe inteira,


podendo ser public ou package-private.
„„ Nível de membro: em que se determina o acesso de atributos ou métodos
da classe, podendo ser public, private, protected, package-private e
default.

Para termos métodos encapsulados, fazemos uso de modificadores de


acesso, ou seja, para acessar atributo ou método que faça parte do encapsu-
lamento, deve-se utilizar get e set, em que set significa que algum atributo
deve ter certo valor; e get é utilizado para recuperar o valor desse atributo.
Na Figura 1, você verá um exemplo de encapsulamento para que possa
compreender melhor. Conforme Moreira Neto (2004), o exemplo a seguir
apresenta uma classe em que existem dados privativos e construtores e dois
métodos (débito e crédito) que podem alterar esses campos. Dessa forma,
consegue-se garantir que todas as contas sejam sempre válidas.

Se o método puder ser usado por outras classes (que não sejam subclasses), use public;
caso contrário, se você quiser que suas subclasses possam alterar algum comporta-
mento, encapsule o comportamento em um método protected. Já se o método trata
de um detalhe de implementação e você não quer que ninguém modifique (nem
mesmo as subclasses), use private.

133
Encapsulamento e modificadores de acesso

public class Conta {

private int numero;

private double saldo;

private double juros;

//metodos de acesso

public double getJuros(){

return juros;

public int getNumero(){

return numero;

public double getSaldo(){

return saldo;

public void setJuros(double juros) {

this.juros = juros;

public void setNumero(int numero) {

this.numero = numero;

//metodos

public void debito(double valor){

this.saldo -= valor;

public void credito(double valor){

this.saldo += valor;

Figura 1. Código encapsulamento.


Fonte: Neto (2004, p. 76).

134
Encapsulamento e modificadores de acesso

Reconhecimento de modificadores e
métodos de acesso
Em POO modificador de acesso é a palavra que define um atributo, método ou
classe e pode ser público, privado ou protegido. Entre os três modificadores
existem quatro níveis de visibilidade: private, default, protected e public. Pú-
blico (public) significa que qualquer classe pode ter acesso; privado (private),
que somente têm acesso métodos da própria classe, podendo manipular o
atributo; protegido (protected) pode ser acessado somente pela própria classe
ou subclasses; e default tem acesso as classes que estiverem no mesmo pacote
que a classe que possui o atributo.
Em geral, os modificadores de acesso são utilizados para privar os atributos
de serem acessados diretamente, e implementam métodos públicos que possam
acessar e alterar os atributos. Esse processo é chamado de encapsulamento.
Métodos privados são utilizados por métodos públicos da mesma classe,
para que o código seja reutilizado em mais de um método. Conforme descrito
anteriormente, o private proíbe o acesso externo, mas também bloqueia a lei-
tura do atributo. Assim, para que possamos recuperar seu valor, precisaremos
utilizar um método público na própria classe que devolverá o valor do atributo.

Construção de aplicação utilizando


técnicas de encapsulamento
Em POO o mecanismo que faz o vínculo entre o código e os dados que são
tratados é chamado de encapsulamento, forma na qual o código é mantido de
maneira segura. Utilizando essa técnica, é possível diminuir a complexidade
do desenvolvimento da aplicação. É fundamental a utilização de modificadores
de acesso para que os códigos possam ser mantidos e novas implementações
sejam criadas, sem que ocorram problemas em níveis de acessos. Esses modi-
ficadores têm a importante missão de controlar o acesso a membros de classes.
A seguir, você verá um exemplo de encapsulamento:

135
Encapsulamento e modificadores de acesso

Acesse o link ou o código a seguir para assistir a um vídeo sobre encapsulamento.

https://qrgo.page.link/1SSvz

136
Encapsulamento e modificadores de acesso

O exemplo da tabela a seguir apresenta um tutorial para você entender melhor a


questão dos modificadores de acesso.

Modifier Class Package Subclass World

Public ✔ ✔ ✔ ✔

Protected ✔ ✔ ✔ ✘

No modifier ✔ ✔ ✘ ✘

Private ✔ ✘ ✘ ✘

Outro exemplo prático pode ser observado a seguir na implementação de uma


classe Animal.
Na classe Gato, você poderá chamar todos os métodos de Animal declarados como
public ou protected e, se as classes estiverem no mesmo pacote, os métodos default.
Dessa forma, Gato não chama os métodos private de Animal. O mesmo raciocínio se
aplica aos atributos.
Você também pode sobrescrever (override), em Gato, os métodos public e protected
de Animal e, se estiver no mesmo pacote, também os métodos default.
Note que você pode ter em Gato um método com a mesma assinatura de um
método private de Animal, mas nesse caso trata-se de um método novo e não uma
nova versão do método de Animal.

Como o método private de Animal não foi sobrescrito por Gato, é ele que será
chamado quando houver uma referência do tipo Animal.

137
Encapsulamento e modificadores de acesso

Referências

HORSTMANN, C. Conceitos de computação com o essencial de C++. 3. ed. Porto Alegre:


Bookman, 2005.
NETO, O. M. Entendendo e dominando o Java. São Paulo: Digerati, 2004.

Leituras recomendadas
BARBOSA, M. A. L. Tipos abstratos de dados e construções encapsuladas: linguagens de
programação. 2012. Disponível em: <http://malbarbo.pro.br/arquivos/2012/1028/11-ti-
pos-abstratos-de-dados-e-construcoes-encapsuladas.pdf>. Acesso em: 17 maio 2018.
HAILTON. Um pouco sobre polimorfismo Java. 2009. Disponível em: <https://www.
devmedia.com.br/encapsulamento-polimorfismo-heranca-em-java/12991>. Acesso
em: 17 maio 2018.
QUAL É A DIFERENÇA entre modificadores public, default, protected e private?
2015. Disponível em: <https://pt.stackoverflow.com/questions/23/qual-%C3%A9-a-
diferen%C3%A7a-entre-modificadores-public-default-protected-e-private>. Acesso
em: 17 maio 2018.

Os links para sites da Web fornecidos neste capítulo foram todos testados, e seu fun-
cionamento foi comprovado no momento da publicação do material. No entanto, a
rede é extremamente dinâmica; suas páginas estão constantemente mudando de
local e conteúdo. Assim, os editores declaram não ter qualquer responsabilidade
sobre qualidade, precisão ou integralidade das informações referidas em tais links.

138
DICA DO PROFESSOR

Podemos dizer que encapsular seria empacotar, esconder, deixar visível apenas aquilo que
interessa ao usuário. Assista, no vídeo, um exemplo prático de encapsulamento e acompanhe o
uso da técnica de encapsulamento.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Em relação ao encapsulamento, marque a afirmativa INCORRETA:

A) Encapsular é organizar os programas em coleções de códigos e dados logicamente


relacionados.

B) Encapsulamentos são colocados em bibliotecas e disponibilizados para reuso em


programas além daqueles para os quais eles foram escritos.

C) Encapsular é deixar disponível para o usuário apenas o que lhe interessa, sem a
necessidade de expor detalhes do código.

D) Encapsular é deixar os métodos inacessíveis para os usuários da classe.

E) Existe outro tipo de encapsulamento, necessário para construir grandes programas, o


encapsulamento de nomeação.

2) Para encapsular um atributo, deixando-o visível apenas para a classe que o contém,
utilizamos qual palavra-chave?

A) Public.

139
B) Static.

C) Void.

D) Não é necessário informar modificador de acesso.

E) Private.

3) A unidade básica de encapsulamento, em Java, é:

A) Pacote.

B) Modificadores de acesso.

C) Classe.

D) Método.

E) Interface pública da classe.

4) Membros declarados com esse modificador de acesso são acessíveis em subclasses da


classe, em subclasses do mesmo pacote e na própria classe:

A) Protected.

B) Private.

C) Public.

140
D) Package.

E) Static.

5) Analise o seguinte código e aponte a afirmativa INCORRETA:

public class Aluno {


private String nome;
protected String Sobrenome;
public int matricula;
public String email;

public String getNome() {


return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getSobrenome() {
return Sobrenome;
}
public void setSobrenome(String Sobrenome) {
this.Sobrenome = Sobrenome;
}
public int getMatricula() {
return matricula;
}
public void setMatricula(int matricula) {
this.matricula = matricula;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;

141
}
}

A) Todos os atributos desta classe poderão ser acessados apenas por seus respectivos
métodos.

B) Os métodos cujo nome é precedido da palavra get são métodos de retorno.

C) Toda classe que possui atributos privados não obriga que cada atributo tenha o método que
fará acesso ao mesmo.

D) É recomendado que todos atributos da classe aluno sejam implementados com o


modificador de acesso private.

E) Ao instanciarmos esta classe através de uma classe de controle, quando chamarmos os


membros através da variável de referência, apenas o atributo nome não estará visível.

NA PRÁTICA

Em programação houve um tempo em que os códigos ficaram muito extensos e, com isso, os
programadores tinham muita dificuldade na organização; portanto, a solução foi dividir em
componentes, os quais os usuários não tinham a necessidade de conhecer a complexidade do
código do componente, apenas informar as entradas corretas para receber resultados. Esta
técnica se chama encapsular o código, que além da organização, trouxe mais segurança, pois
outros programadores usam o componente sem fazer qualquer alteração.

Quando falamos em encapsulamento, podemos ter como exemplo:

Conteúdo interativo disponível na plataforma de ensino!

142
SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Métodos, atributos e classes no Java

Veja neste artigo quais são e como utilizar os modificadores de acesso a métodos, atributos e
classes da linguagem Java.

Conteúdo interativo disponível na plataforma de ensino!

Conceitos de Computação com Java - Compatível com Java 5 & 6 Cay Horstmann, 2009,
5ª Edição

Acompanhe a obra a seguir onde você poderá aprofundar seus conhecimentos sobre os conceitos
e as principais práticas de programação. Dicas uteis sobre as boas práticas da engenharia de
software e etc.

Conceitos de Linguagens de Programação Robert Sebesta, 2018, 11ª Edição

Veja na obra a seguir uma apresentação sobre as construções fundamentais da linguagem de


programação, sendo possível fornecer aos estudantes as ferramentas necessárias para uma
avaliação critica de linguagens existentes e futuras.

143
Introdução ao polimorfismo

APRESENTAÇÃO

Polimorfismo é um dos quatro pilares da programação orientada a objetos. Polimorfismo deriva


do grego “muitas formas”. Em orientação a objetos, seria usar o mesmo método de formas
diferentes. Segundo Horstmann Cay (2009), quando múltiplas classes implementam a mesma
interface, cada classe implementa os métodos da interface de diferentes maneiras, isto é, cada
classe implementa o método de acordo com suas funcionalidades.

Nesta Unidade de Aprendizagem estudaremos o polimorfismo e sua aplicação.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é polimorfismo.


• Identificar interfaces.
• Construir uma aplicação utilizando a prática de polimorfismo.

DESAFIO

Você trabalha como analista/programador em uma fábrica de software e foi solicitada a criação
de uma calculadora com as quatro operações básicas da matemática - soma, subtração,
multiplicação e divisão. Essa calculadora deverá receber apenas dois números de cada vez e a
operação desejada. Para construi-la, você deverá fazer o uso de uma interface com um método
calcular e quatro classes que implementam esta interface, cada classe correspondente a uma
operação. Crie uma classe controle para instanciar as classes das operações. Para executar a
tarefa, utilize uma linguagem de programação orientada a objeto.
Logo após a criação, exporte o projeto em um arquivo zipado.

INFOGRÁFICO

144
Veja no infográfico conceitos de polimorfismo.

CONTEÚDO DO LIVRO

O polimorfismo permite que você “programe no geral”, em vez de “programar no específico”.


Em particular, permite escrever programas que processam objetos que compartilham a mesma
superclasse (direta ou indiretamente), como se todos fossem objetos da superclasse; isso pode
simplificar a programação.
Acompanhe um trecho do livro Android: como programar, o qual serve de base teórica para esta
Unidade de Aprendizagem. Inicie seu estudo pelo tópico Introdução ao polimorfismo e finalize
no tópico Declarando uma classe abstrata e métodos abstratos.
Boa leitura!

145
SEGUNDA EDIÇÃO

com introdução a Java

Paul Deitel • Harvey Deitel • Abbey Deitel


146
Android: Como Programar

G.6 Introdução ao polimorfismo


Continuamos nosso estudo sobre programação orientada a objetos explicando e de-
monstrando o polimorfismo com hierarquias de herança. O polimorfismo permite que
você “programe no geral”, em vez de “programar no específico”. Em particular, o poli-
morfismo permite escrever programas que processam objetos que compartilham a mes-
ma superclasse (direta ou indiretamente), como se todos fossem objetos da superclasse;
isso pode simplificar a programação.
Considere o exemplo de polimorfismo a seguir. Suponha que criemos um progra-
ma para simular o movimento de vários tipos de animais para um estudo biológico. As
classes Peixe, Rã e Pássaro representam os tipos de animais sob investigação. Imagine
que cada classe estende a superclasse Animal, a qual contém um método mover e mantém
a posição atual de um animal como coordenadas x-y. Cada subclasse implementa o mé-
todo mover. Nosso programa mantém um array Animal que contém referências para obje-
tos de várias subclasses de Animal. Para simular os movimentos dos animais, o programa
envia a cada objeto a mesma mensagem, uma vez por segundo – a saber, mover. Cada tipo
específico de Animal responde a uma mensagem mover à sua própria maneira – um Peixe
poderia nadar por um metro, uma Rã poderia pular por um metro e meio e um Pássaro
poderia voar por três metros. Cada objeto sabe como modificar suas coordenadas x-y
apropriadamente para seu tipo específico de movimento. Contar com o fato de que cada
objeto sabe como “fazer a coisa certa” (isto é, fazer o que é adequado para esse tipo de
objeto) em resposta à mesma chamada de método é o principal conceito do polimorfis-
mo. A mesma mensagem (neste caso, mover) enviada para uma variedade de objetos tem
“muitas formas” de resultados – daí o termo polimorfismo.

Programar no específico
Ocasionalmente, ao realizarmos processamento polimórfico, precisamos programar “no
específico”. Vamos demonstrar que um programa pode determinar o tipo de um objeto
no momento da execução e atuar nesse objeto de forma correspondente.

Interfaces
O apêndice continua com uma introdução às interfaces Java. Uma interface descreve
um conjunto de métodos que podem ser chamados em um objeto, mas não fornece
implementações concretas para todos os métodos. Você pode declarar classes que im-
plementam (isto é, fornecem implementações concretas para os métodos de) uma ou
mais interfaces. Cada método de interface deve ser declarado em todas as classes que im-
plementam a interface explicitamente. Quando uma classe implementa uma interface,
todos os objetos dessa classe têm uma relação é um com o tipo de interface, e é garantido
que todos os objetos da classe fornecem a funcionalidade descrita pela interface. Isso
também vale para todas as subclasses dessa classe.
As interfaces são particularmente úteis para atribuir funcionalidade comum a clas-
ses possivelmente não relacionadas. Isso permite que objetos de classes não relacionadas
sejam processados de modo polimórfico – objetos de classes que implementam a mesma
interface podem responder a todas as chamadas de métodos da interface de forma perso-
nalizada. Para demonstrar a criação e o uso de interfaces, modificamos nosso aplicativo
de folha de pagamento para criar um aplicativo de contas a pagar geral que pode calcular
os pagamentos devidos aos funcionários da empresa e os valores das faturas a serem
cobrados por bens adquiridos. Como você vai ver, as interfaces possiblitam recursos
polimórficos semelhantes aos que são possíveis com a herança.

147
Apêndice G Programação orientada a objetos: herança e polimorfismo

G.7 Polimorfismo: um exemplo


Objetos espaciais em um videogame
Suponha que projetemos um videogame que manipula objetos das classes Marciano,
Venusiano, Plutoniano, NaveEspacial e RaioLaser. Imagine que cada classe herda da
superclasse ObjetoEspacial, a qual contém o método desenhar. Cada subclasse im-
plementa esse método. Um gerenciador de tela mantém uma coleção (por exemplo,
um array ObjetoEspacial) de referências para objetos das várias classes. Para atualizar
a tela, o gerenciador envia periodicamente a cada objeto a mesma mensagem – a
saber, desenhar. Contudo, cada objeto responde à sua própria maneira, de acordo
com sua classe. Por exemplo, um objeto Marciano poderia desenhar-se em vermelho
com olhos verdes e com o número apropriado de antenas. Um objeto NaveEspacial
poderia desenhar-se como um disco voador prata luminoso. Um objeto RaioLaser
poderia desenhar-se como um feixe vermelho brilhante na tela. Novamente, a mesma
mensagem (neste caso, desenhar) enviada para uma variedade de objetos tem “muitas
formas” de resultados.
Um gerenciador de tela poderia usar polimorfismo para facilitar a inclusão de
novas classes a um sistema, com modificações mínimas no código do sistema. Suponha
que queiramos adicionar objetos Mercuriano ao nosso videogame. Para isso, construi-
ríamos uma classe Mercuriano que estenderia ObjetoEspacial e forneceria sua própria
implementação do método desenhar. Quando objetos Mercuriano aparecem na coleção
ObjetoEspacial, o código do gerenciador de tela chama o método desenhar, exatamente
como faz para qualquer outro objeto da coleção, independentemente de seu tipo. Assim, os
novos objetos Mercuriano simplesmente “se conectam”, sem qualquer modificação no
código do gerenciador de tela por parte do programador. Portanto, sem modificar o
sistema (a não ser pela construção de novas classes e a modificação do código que cria
novos objetos), você pode usar polimorfismo para convenientemente incluir tipos adi-
cionais que não estavam previstos quando o sistema foi criado.

Observação sobre engenharia de software G.4


O polimorfismo permite que você lide com as generalidades e deixe o ambiente de tempo
de execução tratar dos detalhes específicos. Você pode fazer que os objetos se comportem
de maneiras adequadas sem conhecer seus tipos (desde que os objetos pertençam à mesma
hierarquia de herança).

Observação sobre engenharia de software G.5


O polimorfismo promove a extensibilidade: um software que ativa comportamento
polimórfico é independente dos tipos de objeto para os quais as mensagens são enviadas.
Novos tipos de objeto que podem responder às chamadas de método já existentes podem
ser incorporados a um sistema sem modificar o sistema básico. Somente o código cliente
que instancia novos objetos precisa ser modificado para acomodar os novos tipos.

G.8 Demonstração de comportamento polimórfico


A seção G.4 criou uma hierarquia de classes na qual a classe BasePlusCommissionEmployee
herdava de CommissionEmployee. Os exemplos daquela seção manipulavam objetos
CommissionEmployee e BasePlusCommissionEmployee usando referências a eles para cha-

148
Android: Como Programar

mar seus métodos – apontamos variáveis de superclasse para objetos de superclasse e


variáveis de subclasse para objetos de subclasse. Essas atribuições são naturais e simples
– as variáveis de superclasse são destinadas a fazer referência a objetos de superclasse, e as
variáveis de subclasse são destinadas a fazer referência a objetos de subclasse. Contudo,
conforme você vai ver em breve, outras atribuições são possíveis.
No próximo exemplo, apontamos uma referência de superclasse para um objeto de
subclasse. Então, mostramos como o fato de chamar um método em um objeto de sub-
classe por meio de uma referência de superclasse ativa a funcionalidade da subclasse – o
tipo do objeto referenciado, não o tipo da variável, determina qual método é chamado.
Esse exemplo demonstra que um objeto de uma subclasse pode ser tratado como um obje-
to de sua superclasse, permitindo várias manipulações interessantes. Um programa pode
criar um array de variáveis de superclasse que se referem a objetos de muitos tipos de sub-
classe. Isso é permitido porque cada objeto de subclasse é um objeto de sua superclasse.
Por exemplo, podemos atribuir a referência de um objeto BasePlusCommissionEmployee
a uma variável da superclasse CommissionEmployee, pois um BasePlusCommissionEmployee
é um CommissionEmployee – podemos tratar um BasePlusCommissionEmployee como um
CommissionEmployee.
Conforme você vai aprender mais adiante neste apêndice, não é permitido tratar
um objeto de superclasse como um objeto de subclasse, pois um objeto de superclasse não é
um objeto de qualquer uma de suas subclasses. Por exemplo, não podemos atribuir a re-
ferência de um objeto CommissionEmployee a uma variável da subclasse BasePlusCommis-
sionEmployee, pois um CommissionEmployee não é um BasePlusCommissionEmployee – um
CommissionEmployee não tem uma variável de instância baseSalary e não tem métodos
setBaseSalary e getBaseSalary. A relação é um se aplica apenas para cima na hierarquia,
de uma subclasse para suas superclasses diretas e indiretas, e não vice-versa (isto é, não
para baixo na hierarquia, de uma superclasse para suas subclasses).
O compilador Java permite a atribuição de uma referência de superclasse para uma
variável de subclasse se convertermos explicitamente a referência de superclasse para o
tipo da subclasse – uma técnica que discutimos na seção G.10. Por que desejaríamos
fazer tal atribuição? Uma referência de superclasse só pode ser usada para chamar os mé-
todos declarados na superclasse – chamar métodos exclusivos da subclasse por meio de
uma referência de superclasse resulta em erros de compilação. Se um programa precisa
efetuar uma operação específica da subclasse em um objeto de subclasse referenciado por
uma variável de superclasse, ele deve primeiro converter a referência de superclasse em
uma referência de subclasse, por meio de uma técnica conhecida como downcasting.
Isso permite que o programa chame métodos de subclasse que não estão na superclasse.
Mostramos um exemplo de downcasting na seção G.10.
O exemplo da Fig. G.13 demonstra três maneiras de usar variáveis de superclasse
e subclasse para armazenar referências para objetos de superclasse e subclasse. As duas
primeiras são simples – como na seção G.4, atribuímos uma referência de superclasse
a uma variável de superclasse e uma referência de subclasse a uma variável de subclasse.
Então, demonstramos a relação entre subclasses e superclasses (isto é, a relação é um),
atribuindo uma referência de subclasse a uma variável de superclasse. Esse programa
usa as classes CommissionEmployee e BasePlusCommissionEmployee da Fig. G.10 e da Fig.
G.11, respectivamente.

149
Apêndice G Programação orientada a objetos: herança e polimorfismo

1 // Fig. G.13: PolymorphismTest.java


2 // Atribuindo referências de superclasse e subclasse a
3 // variáveis de superclasse e subclasse.
4
5 public class PolymorphismTest
6 {
7 public static void main( String[] args )
8 {
9 // atribui referência de superclasse à variável de superclasse
10 CommissionEmployee commissionEmployee = new CommissionEmployee(
11 "Sue", "Jones", "222-22-2222", 10000, .06 );
12
13 // atribui referência de subclasse à variável de subclasse
14 BasePlusCommissionEmployee basePlusCommissionEmployee =
15 new BasePlusCommissionEmployee(
16 "Bob", "Lewis", "333-33-3333", 5000, .04, 300 );
17
18 // chama toString no objeto de superclasse usando variável de superclasse
19 System.out.printf( "%s %s:\n\n%s\n\n",
20 "Call CommissionEmployee's toString with superclass reference ",
21 "to superclass object", commissionEmployee.toString() );
22
23 // chama toString no objeto de subclasse usando variável de subclasse
24 System.out.printf( "%s %s:\n\n%s\n\n",
25 "Call BasePlusCommissionEmployee's toString with subclass",
26 "reference to subclass object",
27 basePlusCommissionEmployee.toString() );
28
29 // chama toString no objeto de subclasse usando variável de superclasse
30 CommissionEmployee commissionEmployee2 =
31 basePlusCommissionEmployee;
32 System.out.printf( "%s %s:\n\n%s\n",
33 "Call BasePlusCommissionEmployee's toString with superclass",
34 "reference to subclass object", commissionEmployee2.toString() );
35 } // fim de main
36 } // fim da classe PolymorphismTest

Call CommissionEmployee's toString with superclass reference to superclass


object:

commission employee: Sue Jones


social security number: 222-22-2222
gross sales: 10000.00
commission rate: 0.06

Call BasePlusCommissionEmployee's toString with subclass reference to


subclass object:

base-salaried commission employee: Bob Lewis


social security number: 333-33-3333
gross sales: 5000.00
commission rate: 0.04
base salary: 300.00

Call BasePlusCommissionEmployee's toString with superclass reference to


subclass object:

base-salaried commission employee: Bob Lewis


social security number: 333-33-3333
gross sales: 5000.00
commission rate: 0.04
base salary: 300.00

Figura G.13 Atribuindo referências de superclasse e subclasse a variáveis de superclasse e subclasse.

150
Android: Como Programar

Na Fig. G.13, as linhas 10 e 11 criam um objeto CommissionEmployee e atri-


buem sua referência a uma variável de CommissionEmployee. As linhas 14 a 16 criam
um objeto BasePlusCommissionEmployee e atribuem sua referência a uma variável de
BasePlusCommissionEmployee. Essas atribuições são naturais – por exemplo, a principal fina-
lidade de uma variável de CommissionEmployee é armazenar uma referência para um objeto
CommissionEmployee. As linhas 19 a 21 usam commissionEmployee para chamar toString ex-
plicitamente. Como commissionEmployee se refere a um objeto CommissionEmployee, é cha-
mada a versão de toString da superclasse CommissionEmployee. Do mesmo modo, as linhas
24 a 27 usam basePlusCommissionEmployee para chamar toString explicitamente no objeto
BasePlusCommissionEmployee. Isso chama a versão de toString da subclasse BasePlusCom-
missionEmployee. Então, as linhas 30 e 31 atribuem a referência do objeto de subclasse
basePlusCommissionEmployee a uma variável da superclasse CommissionEmployee, a qual as
linhas 32 a 34 usam para chamar o método toString. Quando uma variável de superclasse
contém uma referência para um objeto de subclasse e essa referência é usada para chamar um
método, é chamada a versão da subclasse do método. Assim, commissionEmployee2.toString()
na linha 34 chama na verdade o método toString da classe BasePlusCommissionEmployee. O
compilador Java permite esse “cruzamento” porque um objeto de uma subclasse é um ob-
jeto de sua superclasse (mas não vice-versa). Quando o compilador encontra uma chamada
de método feita por meio de uma variável, ele determina se o método pode ser chamado
verificando o tipo de classe da variável. Se essa classe contém a declaração de método correta
(ou herda uma), a chamada é compilada. No momento da execução, o tipo do objeto ao
qual a variável se refere determina o método realmente usado. Esse processo, denominado
vinculação dinâmica, está discutido em detalhes na seção G.10.

G.9 Classes e métodos abstratos


Quando pensamos em uma classe, presumimos que os programas vão criar objetos desse
tipo. Às vezes, é interessante declarar classes – denominadas classes abstratas – para as
quais você nunca pretende criar objetos. Como elas são usadas apenas como superclasses
nas hierarquias de herança, nos referimos a elas como superclasses abstratas. Essas clas-
ses não podem ser usadas para instanciar objetos, pois, como veremos em breve, as classes
abstratas são incompletas. As subclasses devem declarar as “partes ausentes” para se torna-
rem classes “concretas”, a partir das quais você pode instanciar objetos. Caso contrário, es-
sas subclasses também serão abstratas. Demonstramos as classes abstratas na seção G.10.

Finalidade das classes abstratas


A finalidade de uma classe abstrata é fornecer uma superclasse apropriada a partir da
qual outras classes podem herdar e, assim, compartilhar um projeto comum. Na hie-
rarquia Forma da Fig. G.3, por exemplo, as subclasses herdam a noção do que significa
ser uma Forma – talvez atributos comuns, como localização, cor e espessuraDaBorda, e
comportamentos como desenhar, mover, redimensionar e mudarCor. As classes que po-
dem ser usadas para instanciar objetos são denominadas classes concretas. Essas classes
fornecem implementações de cada método que declaram (algumas das implementações
podem ser herdadas). Por exemplo, poderíamos produzir as classes concretas Círculo,
Quadrado e Triângulo a partir da superclasse abstrata FormaBidimensional. Do mesmo
modo, poderíamos produzir as classes concretas Esfera, Cubo e Tetraedro a partir da
superclasse abstrata FormaTridimensional. As superclasses abstratas são gerais demais para
criar objetos reais – elas especificam apenas o que é comum entre as subclasses. Precisa-
mos ser mais específicos antes de podermos criar objetos. Por exemplo, se você envia a

151
Apêndice G Programação orientada a objetos: herança e polimorfismo

mensagem desenhar para a classe abstrata FormaBidimensional, a classe sabe que formas
bidimensionais devem ser desenhadas, mas não sabe a forma específica a desenhar; por-
tanto, não pode implementar um método desenhar real. As classes concretas fornecem
os detalhes específicos que as tornam adequadas para instanciar objetos.
Nem todas as hierarquias contêm classes abstratas. No entanto, muitas vezes você
vai escrever código cliente que utiliza apenas tipos de superclasse abstrata para reduzir as
dependências do código a um intervalo de tipos de subclasse. Por exemplo, você pode
escrever um método com um parâmetro de um tipo de superclasse abstrata. Quando
chamado, esse método pode receber um objeto de qualquer classe concreta que estenda
direta ou indiretamente a superclasse especificada como tipo do parâmetro.
Às vezes, as classes abstratas constituem vários níveis de uma hierarquia. Por exem-
plo, a hierarquia Forma da Fig. G.3 começa com a classe abstrata Forma. No nível seguinte
da hierarquia estão as classes abstratas FormaBidimensional e FormaTridimensional. O
próximo nível declara classes concretas para FormasBidimensionais (Círculo, Quadrado e
Triângulo) e para FormasTridimensionais (Esfera, Cubo e Tetraedro).

Declarando uma classe abstrata e métodos abstratos


Você torna uma classe abstrata declarando-a com a palavra-chave abstract. Normal-
mente, uma classe abstrata contém um ou mais métodos abstratos. Um método abstra-
to é o que tem a palavra-chave abstract em sua declaração, como em
public abstract void draw(); // método abstrato

Os métodos abstratos não fornecem implementações. Uma classe que contém


quaisquer métodos abstratos deve ser explicitamente declarada como abstract, mesmo
que essa classe contenha alguns métodos concretos (não abstratos). Cada subclasse con-
creta de uma superclasse abstrata também deve fornecer implementações concretas de
cada um dos métodos abstratos da superclasse. Construtores e métodos estáticos não
podem ser declarados como abstract. Construtores não são herdados; portanto, um
construtor abstrato nunca poderia ser implementado. Embora os métodos estáticos não
private sejam herdados, eles não podem ser sobrescritos. Como os métodos abstratos se
destinam a ser sobrescritos para que possam processar objetos de acordo com seus tipos,
não faria sentido declarar um método estático como abstract.

Observação sobre engenharia de software G.6


Uma classe abstrata declara atributos e comportamentos comuns (tanto abstratos como
concretos) das várias classes de uma hierarquia de classes. Normalmente, uma classe abs-
trata contém um ou mais métodos abstratos que as subclasses devem sobrescrever, caso de-
vam ser concretas. As variáveis de instância e os métodos concretos de uma classe abstrata
estão sujeitos às regras normais da herança.

Usando classes abstratas para declarar variáveis


Embora não possamos instanciar objetos de superclasses abstratas, em breve você vai ver
que podemos usar superclasses abstratas para declarar variáveis que podem armazenar re-
ferências para objetos de qualquer classe concreta derivada dessas superclasses abstratas.
Normalmente, os programas usam tais variáveis para manipular objetos de subclasse de
forma polimórfica. Também é possível usar nomes de superclasses abstratas para chamar
métodos estáticos declarados nessas superclasses.
Considere outra aplicação de polimorfismo. Um programa de desenho precisa
exibir muitas formas, incluindo tipos de novas formas que você vai adicionar ao sistema

152
Android: Como Programar

depois de escrever o programa de desenho. Talvez esse programa precise exibir formas
como Círculos, Triângulos, Retângulos ou outras que derivem da classe abstrata Forma.
O programa de desenho usa variáveis de Forma para gerenciar os objetos que são exibi-
dos. Para desenhar qualquer objeto dessa hierarquia de herança, o programa de desenho
usa uma variável da superclasse Forma contendo uma referência para o objeto de sub-
classe, a fim de chamar o método desenhar do objeto. Esse método é declarado como
abstract na superclasse Forma; portanto, cada subclasse concreta deve implementar o
método desenhar de uma maneira específica para essa forma – cada objeto da hierarquia
de herança Forma sabe como se desenhar. O programa de desenho não precisa se preocupar
com o tipo de cada objeto ou se encontrou objetos desse tipo.

Figura G.14 Diagrama de classe em UML da hierarquia Employee.

153
DICA DO PROFESSOR

Acompanhe no vídeo o uso prático de polimorfismo.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) O polimorfismo é um dos quatro pilares da programação de orientação a objetos.


Portanto, marque a opção que melhor descreva o conceito de polimorfismo.

A) Quando uma classe herda membros de outra classe.

B) Quando uma classe possui métodos abstratos.

C) Quando cada classe implementa o mesmo método de diferentes maneiras.

D) Quando uma subclasse usa um método da superclasse.

E) Quando uma classe possui um ou mais métodos sem retorno.

2) Para podermos entender melhor e trabalharmos com polimorfismo, é de extrema


importância sabermos alguns conceitos, como as "Interfaces". Portanto, marque a
alternativa que melhor define interfaces em programação orientada a objetos.

A) São classes que possuem apenas atributos.

B) Interfaces são classes que não podem ser instanciadas.

154
C) Diferentemente de uma classe, um tipo de interface não fornece nenhuma implementação.

D) Interfaces possuem variáveis de instância e métodos abstratos.

E) Interfaces são classes que possuem métodos que não podem ser implementados por outras
classes.

3) Em relação ao polimorfismo, marque a alternativa incorreta.

A) O polimorfismo permite que programemos no geral, e não no específico.

B) Programar no específico é quando o programa determina o tipo de objeto no momento da


execução.

C) Para trabalharmos com polimorfismo, podemos herdar métodos de uma superclasse ou


implementarmos uma interface.

D) A técnica de polimorfismo não permite que alteremos a implementação de um método.

E) Novos tipos de objeto que podem responder às chamadas de método já existentes podem
ser incorporados ao sistema sem alterar o sistema básico.

4) Qual a relação do polimorfismo com interfaces e superclasses?

A) Há polimorfismo quando uma subclasse herda membros de uma superclasse.

B) Quando uma classe herda membros de uma interface, ativa um comportamento


polimórfico.

C) Quando um método existente em uma interface é usado por uma classe que não altera a

155
implementação do método, temos um comportamento de polimorfismo.

D) Quando uma classe implementa uma interface, ela se obriga a utilizar e implementar todos
os métodos da interface, com isso, temos um comportamento polimórfico.

E) Quando uma classe implementa uma interface, não há necessidade de utilizar todos os seus
métodos. Entretanto, para que haja um comportamento polimórfico, é necessário
implementar os métodos utilizados de acordo com sua necessidade.

5) Analise o código abaixo e marque a alternativa correta.

156
A) O código 1 refere-se à criação de uma classe abstrata.

B) O código 2 está incorreto, pois o método locomover pertence à interface "InterfaceTeste".


Quando a classe "Formiga" implementou a interface, a mesma alterou a implementação do
método.

C) O código 3 está escrito de forma correta.

D) O código 4 está escrito de forma errada, pois, para utilizarmos uma interface, devemos
colocar a palavra-chave implements, não extends.

E) Sendo o código 1 a interface, podemos concluir que nos códigos 2, 3 e 4, temos um


comportamento polimórfico.

NA PRÁTICA

Polimorfismo em orientação a objetos refere-se à condição que um método tem de assumir


várias formas, isto é, é a utilização de um método com a mesma assinatura, entretanto,
executando ações específicas para a classe que o contém. Uma das vantagens do polimorfismo é
menor trabalho e mais localizado.
Um exemplo clássico é o método print no Java. É chamado em diferentes classes, mas o
resultado impresso na tela será de acordo com a necessidade daquela classe.

157
SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Conceitos de computação com Java

HORSTMANN, C. Conceitos de computação com Java. 5.ed. Porto Alegre: Bookman, 2009.

Aula - Polimorfismo

Veja um pouco mais sobre polimorfismo no vídeo abaixo.

Conteúdo interativo disponível na plataforma de ensino!

Java para iniciantes: crie, compile e execute programas Java rapidamente.

Confira o livro Java para iniciantes no capítulo 7 (246) a relação de Herança e seu métodos que
dão suporte ao polimorfismo.

158
Sobrescrita

APRESENTAÇÃO

A sobrescrita de métodos em orientação a objetos está diretamente ligada à herança. A


sobrescrita se dá ao passo da existência de um método na classe-mãe, e esse método é herdado
pela classe-filha, sendo implementado um comportamento diferente, isto é, o método continua
com a assinatura, mas seu comportamento muda. Sobrescrever um método significa substituir a
implementação da superclasse daquele método com a sua própria. Assinaturas são idênticas,
mas o tipo de retorno varia de maneira particular.

Nesta Unidade de Aprendizagem conheceremos a sobrescrita e sua aplicação.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é sobrescrita e assinatura de método.


• Identificar herança.
• Construir uma aplicação com uma estrutura de herança fazendo uso de sobrescrita de
métodos.

DESAFIO

Você trabalha como analista/programador em uma fábrica de software e foi encarregado de criar
uma aplicação para realizar o cálculo da área de determinadas figuras geométricas,
especificamente retângulo e triângulo retângulo. Sua tarefa é criar as classes-modelo utilizando
técnicas com herança e sobrescrita de método e uma classe para executar a aplicação. Utilize
uma linguagem de programação orientada a objeto com um IDE para auxiliar na organização do
projeto. (Linguagem recomendada: java com IDE NetBeans.)

Coloque o nome do projeto de “CalculoArea” e encaminhe o arquivo zipado como anexo.

159
INFOGRÁFICO

Veja no infográfico os conceitos de sobrescrita.

CONTEÚDO DO LIVRO

Um método pode ser redefinido em uma subclasse somente se é acessível na superclasse. Se o


método não é acessível na superclasse, então o método na subclasse não sobrescreve o método
na superclasse, mesmo que ele tenha a mesma assinatura.

Acompanhe um trecho do livro A linguagem de programação Java, que serve de base teórica
para esta Unidade de Aprendizagem. Faça o estudo do tópico Acessibilidade e sobrescrita de
métodos.

Boa leitura!

160
A LINGUAGEM DE PROGRAMAÇÃO JAVA

Todos esses contratos requerem consideração e projeto cuidadoso.

18.3.1 Acessibilidade e Sobrescrita de Métodos


Um método pode ser redefinido em uma subclasse somente se o método é acessível na
superclasse. Se o método não é acessível na superclasse então o método na subclasse não
sobrescreve o método na superclasse, mesmo que ele tenha a mesma assinatura. Quando
um método é invocado durante a execução o sistema tem que considerar a acessibilidade
do método ao decidir qual implementação do método deve ser executada.
O seguinte exemplo inventado deve tornar isto mais claro. Suponha que temos uma clas-
se AbstractBase declarada no pacote P1:
package P1;
public abstract class AbstractBase {

private void pri() { print("AbstractBase.pri()"); }


void pac() { print("AbstractBase.pac()"); }
protected void pro() { print("AbstractBase.pro()"); }
public void pub() { print("AbstractBase.pub()"); }

public final void show() {


pri();
pac();

161
CAPÍTULO 18 • PACOTES

pro();
pub();
}
}
Temos quatro métodos, cada um com um modificador de acesso diferente, e cada um
simplesmente identifica a si mesmo. O método show invoca cada um destes métodos para
o objeto atual. Vamos mostrar qual implementação de cada método é invocada quando
aplicada a objetos de diferentes subclasses.
Agora definimos a classe Concrete1, que estende AbstractBase mas vive no paco-
te P2:
package P2;

import P1.AbstractBase;

public class Concrete1 extends AbstractBase {


public void pri() { print("Concrete1.pri()"); }
public void pac() { print("Concrete1.pac()"); }
public void pro() { print("Concrete1.pro()"); }
public void pub() { print("Concrete1.pub()"); }
}
Esta classe redeclara cada um dos métodos da superclasse e altera a sua implementação
para relatar que eles estão na classe Concrete1. Ela também altera seus acessos para pu-
blic, de modo que eles estão acessíveis a todos os outros códigos. Executar o código
new Concrete1().show();
produz a seguinte saída:
AbstractBase.pri()
AbstractBase.pac()
Concrete1.pro()
Concrete1.pub()
Visto que o método pri é inacessível a subclasses (ou a qualquer outra classe), a imple-
mentação de AbstractBase é sempre a invocada por show. O método pac de Abs-
tractBase não é acessível em Concrete1, e assim a implementação de pac em Con-
crete1 não sobrescreve aquela em AbstractBase, e portanto é AbstractBase.pac
que show invoca. Ambos os métodos pro e pub são acessíveis em Concrete1 e são so-
brescritos, e assim a implementação de Concrete1 é usada em show.
A seguir definimos a classe Concrete2, que estende Concrete1 mas está no mesmo
pacote P1 que AbstractBase2:
package P1;

import P2.Concrete1;

public class Concrete2 extends Concrete1 {


public void pri() { print("Concrete2.pri()"); }

2
Ter uma hierarquia de herança que navega dentro e fora de um pacote é geralmente uma idéia muito ruim. É usada aqui pura-
mente para ilustração.

162
A LINGUAGEM DE PROGRAMAÇÃO JAVA

public void pac() { print("Concrete2.pac()"); }


public void pro() { print("Concrete2.pro()"); }
public void pub() { print("Concrete2.pub()"); }
}
Cada método em Concrete2 sobrescreve a versão de Concrete1 porque elas são todas
públicas e portanto acessíveis. Além disso, porque Concrete2 está no mesmo pacote que
AbstractBase, o método AbstractBase.pac é acessível em Concrete2 e assim é so-
brescrito por Concrete2.pac. Invocar show para um objeto Concrete2 imprime
AbstractBase.pri()
Concrete2.pac()
Concrete2.pro()
Concrete2.pub()
Finalmente, definimos a classe Concrete3, que estende Concrete2 mas está em um
pacote P3 diferente:
package P3;

import P1.Concrete2;

public class Concrete3 extends Concrete2 {


public void pri() { print("Concrete3.pri()"); }
public void pac() { print("Concrete3.pac()"); }
public void pro() { print("Concrete3.pro()"); }
public void pub() { print("Concrete3.pub()"); }
}
Invocar show para um objeto Concrete3 imprime
AbstractBase.pri()
Concrete3.pac()
Concrete3.pro()
Concrete3.pub()
Aqui o método Concrete3.pac aparenta ter sobrescrito o inacessível AbstractBa-
se.pac. De fato, Concrete3.pac sobrescreve Concrete2.pac, e Concrete2.pac
sobrescreve AbstractBase.pac – portanto, Concrete3.pac transitivamente sobres-
creve AbstractBase.pac. Ao declarar pac como public, Concrete2 o torna acessí-
vel e passível de sobrescrita por qualquer subclasse3.

3
Isto ilustra porque navegar dentro e fora de um pacote pode ser confuso e deve ser evitado.

163
DICA DO PROFESSOR

Assista o vídeo e veja a sobrescrita de métodos em orientação a objetos.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Um método pode ser sobrescrito apenas quando:

A) Tem o modificador de acesso private.

B) Tem o mesmo nome da classe a qual pertence.

C) É um construtor.

D) É acessível.

E) Tem o mesmo tipo de retorno.

2) O que significa sobrescrever um método?

A) Substituir a implementação do método da superclasse.

B) Escrever um método herdado com mesmo tipo de retorno, mas com nome diferente.

C) As assinaturas devem ser diferentes. Todavia, a implementação deve ser igual.

164
D) As assinaturas e implementações devem ser diferentes daquela na superclasse.

E) Escrever o método com mesmo nome modificando apenas seus parâmetros.

3) Marque a alternativa correta.

A) Os métodos que sobrescrevem não possuem especificadores de acesso.

B) Uma subclasse não pode alterar o acesso aos métodos da superclasse.

C) Um método declarado como protected na superclasse pode ser declarado public na


subclasse.

D) Um método declarado como protected na superclasse pode ser declarado private na


subclasse.

E) O método que sobrescreve não pode ser final.

4) Analise o código abaixo e marque a afirmativa incorreta.

public class Animal{


public void locomover(){
System.out.println("Se locomove");
}
}

public class Peixe extendas Animal {


public void locomover(){
System.out.println("Nada");
}
}

165
A) A classe Peixe é uma subclasse de Animal.

B) A classe Animal é uma classe genérica.

C) Ao instanciar a classe Peixe, e através de uma variável de referência chamar o método


locomover(), a saída que teremos é : Nada.

D) Ao instanciar a classe Peixe, e através de uma variável de referência chamar o método


locomover(), a saída que teremos é : Se locomove.

E) O método locomover(), herdado pela classe Peixe, foi sobrescrito, pois sua assinatura
continua a mesma e apenas seu comportamento foi alterado.

5) Em relação à herança e sobrescrita, marque a alternativa incorreta.

A) Sobrescrita de métodos permite a você estender código existente.

B) Um método pode ser sobrescrito somente se ele é acessível.

C) O método da subclasse não sobrescreve o método private da superclasse.

D) Invocações de métodos private sempre invocam a implementação do método declarado na


classe atual.

E) Uma invocação externa do método da subclasse (assumindo que ele é acessível fora de sua
classe) resulta na invocação da implementação da superclasse.

NA PRÁTICA

Veja, no vídeo a seguir, algumas dicas sobre sobrescrita de método.


166
Conteúdo interativo disponível na plataforma de ensino!

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Universidade XTI - JAVA - 049 - Polimorfismo, Sobrescrita de Métodos

Aprenda os conceitos de Polimorfismo, Sobrescrita de Métodos através de exemplos simples.

Conteúdo interativo disponível na plataforma de ensino!

Conceitos de Computação com Java - Compatível com Java 5 & 6 Cay Horstmann, 2009,
5ª Edição

Acompanhe a obra a seguir onde você poderá aprofundar seus conhecimentos sobre os conceitos
e as principais práticas de programação, dicas uteis sobre as boas práticas de engenharia de
software e etc.

Artigo Sobrecarga e sobreposição de métodos em orientação a objetos

Veja nesse artigo como funciona a sobrecarga e sobreposição de métodos em orientação a


objetos e como esses dois conceitos podem nos ajudar na hora de programar.

Conteúdo interativo disponível na plataforma de ensino!

167
Tratamento de exceções

APRESENTAÇÃO

Exceções são erros que ocorrem em tempo de execução. Usando uma linguagem de
programação, você pode tratar erros de tempo de execução de forma estruturada e controlada.
As exceções podem ser uma simples divisão por zero ou a resposta a algum erro interno. Tratar
exceções também impede que nosso programa possa terminar de forma anormal.

Nesta Unidade de Aprendizagem veremos o tratamento de exceções e a importância desta


técnica para sua aplicação.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Analisar tratamento de exceções.


• Usar try e catch.
• Construir uma aplicação utilizando as técnicas de tratamento de exceção.

DESAFIO

Exceções podem ocorrer na execução de uma aplicação. Podemos citar como exemplo o fato de
uma entrada de dados lançada pelo usuário, que deveria ser um número inteiro, e, por algum
motivo, o usuário digita um caractere. Caso não seja tratado, a aplicação pode terminar de forma
anormal.

Você trabalha como analista/programador em uma fábrica de software. Um dos clientes está
com um problema; sua aplicação é uma calculadora que funciona recebendo dois números, e o
cliente informa uma das quatro operações básicas da matemática (adição, subtração,
multiplicação e divisão). O que ocorre é que quando o usuário digita um valor que não seja um
número decimal, a aplicação gera um erro e encerra. Sua tarefa é implementar um tratamento de
exceções nesta aplicação.

168
INFOGRÁFICO

O infográfico a seguir mostra o conceito de tratamento de exceções.

CONTEÚDO DO LIVRO

Uma exceção é um erro que ocorre no tempo de execução. Usando o subsistema Java de
tratamento de exceções, você pode tratar erros de tempo de execução de maneira estruturada e
controlada.

Acompanhe um trecho do livro Java para iniciantes, obra que serve de base teórica para esta
Unidade de Aprendizagem e traz uma abordagem sobre tratamento de exceções. Inicie o estudo
no tópico Principais habilidades e conceitos e finalize ao final de Consequências de uma
exceção não capturada.

169
Capítulo 9 Tratamento de exceções

Principais habilidades e conceitos


• Conhecer a hierarquia de exceções
• Usar try e catch
• Entender os efeitos de uma exceção não capturada
• Usar várias instruções catch
• Capturar exceções de subclasse
• Aninhar blocos try
• Lançar uma exceção
• Saber os membros de Throwable
• Usar finally
• Usar throws
• Conhecer as exceções internas Java
• Criar classes de exceção personalizadas

.....................................................................................................................................

E ste capítulo discutirá o tratamento de exceções. Uma exceção é um erro que


ocorre no tempo de execução. Usando o subsistema Java de tratamento de exce-
ções, você pode tratar erros de tempo de execução de maneira estruturada e controla-
da. Embora a maioria das linguagens de programação modernas ofereça algum tipo
de tratamento de exceções, o suporte Java é fácil de usar e flexível.
A principal vantagem do tratamento de exceções é que ele automatiza grande
parte do código de tratamento de erros que antigamente tinha que ser inserido “à mão”
em qualquer programa grande. Por exemplo, em algumas linguagens de computador
mais antigas, os códigos de erro são retornados quando um método falha e esses valo-
res devem ser verificados manualmente sempre que o método é chamado. Essa aborda-
gem é, ao mesmo tempo, tediosa e propensa a erros. O tratamento de exceções otimiza
o tratamento de erros, permitindo que o programa defina um bloco de código, chama-
do tratador de exceções, executado automaticamente quando um erro ocorre. Não é
necessário verificar manualmente o sucesso ou a falha de cada chamada de método ou
operação específica. Se um erro ocorrer, ele será processado pelo tratador de exceções.
Outra razão que torna o tratamento de exceções importante é Java definir ex-
ceções padrão para erros que são comuns nos programas, como a divisão por zero
ou um arquivo não encontrado. Para reagir a esses erros, seu programa deve estar
alerta a esse tipo de exceção e tratá-las. Além disso, a biblioteca de APIs Java usa
intensamente exceções.
No fim das contas, ser um programador de Java bem-sucedido significa ser
plenamente capaz de navegar no subsistema de tratamento de exceções Java.

170
Java para Iniciantes

Hierarquia de exceções
Em Java, todas as exceções são representadas por classes e todas as classes de exce-
ções são derivadas de uma classe chamada Throwable. Logo, quando uma exceção
ocorre em um programa, um objeto de algum tipo de classe de exceção é gerado.
Há duas subclasses diretas de Throwable: Exception e Error. As exceções de tipo
Error estão relacionadas a erros que ocorrem na própria máquina virtual Java e não
nos programas. Esses tipos de exceções fogem ao nosso controle e geralmente os
programas não lidam com elas. Portanto, não serão descritas aqui.
Erros que resultam da atividade do programa são representados por subclasses
de Exception. Por exemplo, erros de divisão por zero, que excedem os limites do
array e relacionados a arquivos se enquadram nessa categoria. Em geral, os progra-
mas devem tratar exceções desses tipos. Uma subclasse importante de Exception é
RuntimeException, que é usada para representar vários tipos comuns de erros de
tempo de execução.

Fundamentos do tratamento de exceções


O tratamento de exceções Java é gerenciado por cinco palavras-chave: try, catch,
throw, throws e finally. Elas formam um subsistema interligado em que o uso de
uma implica o uso de outra. No decorrer deste capítulo, examinaremos cada palavra-
-chave com detalhes. No entanto, é útil termos desde o início uma compreensão geral
do papel que cada uma desempenha no tratamento de exceções. Resumidamente,
veja como funcionam.
As instruções do programa que você quiser monitorar em busca de exceções
ficarão dentro de um bloco try. Se uma exceção ocorrer dentro do bloco try, ela
será lançada. Seu código poderá capturar essa exceção usando catch e tratá-la de
alguma maneira racional. Exceções geradas pelo sistema são lançadas automati-
camente pelo sistema de tempo de execução Java. Para lançar manualmente uma
exceção, use a palavra-chave throw. Em alguns casos, uma exceção que é lançada
para fora de um método deve ser especificada como tal por uma cláusula throws.
Qualquer código que deva ser executado ao sair de um bloco try deve ser inserido
em um bloco finally.

Pergunte ao especialista
P: Para não deixar dúvidas, você poderia descrever novamente as condições que
fazem uma exceção ser gerada?
R: Exceções são geradas de três maneiras diferentes. Em primeiro lugar, a Máquina Vir-
tual Java pode gerar uma exceção em reposta a algum erro interno sobre o qual não
tenhamos controle. Normalmente, o programa não trata esses tipos de exceções. Em
segundo lugar, exceções padrão, como as correspondentes à divisão por zero ou índi-
ces fora dos limites de um array, são geradas por erros no código do programa. Temos
que tratar essas exceções. Em terceiro lugar, podemos gerar manualmente uma exce-
ção usando a instrução throw. Independentemente de como uma exceção for gerada,
ela será tratada da mesma forma.

171
Capítulo 9 Tratamento de exceções

Usando try e catch


As palavras-chave try e catch formam a base do tratamento de exceções. Elas fun-
cionam em conjunto, ou seja, você não pode ter um catch sem ter um try. Esta é a
forma geral dos blocos try/catch de tratamento de exceções:
try {
// bloco de código cujos erros estão sendo monitorados
}
catch (TipoExceç1 obEx) {
// tratador de TipoExceç1
}
catch (TipoExceç2 obEx) {
// tratador de TipoExceç2
}
.
.
.
Aqui, TipoExceç é o tipo de exceção que ocorreu. Quando uma exceção é lançada,
ela é capturada pela instrução catch correspondente, que então a processa. Como a
forma geral mostra, podemos ter mais de uma instrução catch associada a uma ins-
trução try. O tipo da exceção determina que instrução catch será executada. Isto é,
se o tipo de exceção especificado por uma instrução catch coincidir com o da exce-
ção ocorrida, essa instrução catch será executada (e todas as outras serão ignoradas).
Quando uma exceção é capturada, obEx recebe seu valor.
Agora um ponto importante: se nenhuma exceção for lançada, o bloco try ter-
minará normalmente e todas as suas instruções catch serão ignoradas. A execução
será retomada na primeira instrução após o último catch. Logo, as instruções catch
só são executadas quando ocorre uma exceção.

NOTA
A partir de JDK 7, há outra forma de instrução try que dá suporte ao gerenciamento
automático de recursos. Essa nova forma de try se chama try-with-resources. Ela é
descrita no Capítulo 10, no contexto do gerenciamento de fluxos de I/O (como os
conectados a um arquivo), porque os fluxos são um dos recursos mais usados.

Exemplo de exceção simples


Este é um exemplo simples que ilustra como monitorar uma exceção e capturá-la.
Como você sabe, é um erro tentar indexar um array além de seus limites. Quando
isso ocorre, a JVM lança uma ArrayIndexOutOfBoundsException. O programa a
seguir gera intencionalmente essa exceção e então a captura:
// Demonstra o tratamento de exceções.
class ExcDemo1 {
public static void main(String args[]) {
int nums[] = new int[4];

try { Cria um bloco try.

172
Java para Iniciantes

System.out.println("Before exception is generated.");

// Gera um exceção de índice fora dos limites.


nums[7] = 10; Tenta indexar
System.out.println("this won't be displayed"); excedendo o
} limite de nums.
catch (ArrayIndexOutOfBoundsException exc) { Captura erros nos
// captura a exceção limites do array.
System.out.println("Index out-of-bounds!");
}
System.out.println("After catch statement.");
}
}

Esse programa exibirá a saída abaixo:


Before exception is generated.
Index out-of-bounds!
After catch statement.

Embora bem curto, o programa anterior ilustra vários pontos-chave do trata-


mento de exceções. Em primeiro lugar, o código cujos erros você quer monitorar
está dentro de um bloco try. Em segundo lugar, quando ocorre uma exceção (nesse
caso, pela tentativa de indexar nums além de seus limites), ela é lançada fora do
bloco try e capturada pela instrução catch. Nesse ponto, o controle passa para catch
e o bloco try é encerrado. Isto é, catch não é chamada. Em vez disso, a execução do
programa é transferida para ela. Logo, a instrução println( ) que vem após o índice
fora do limite nunca será executada. Após a instrução catch ser executada, o controle
do programa continua nas instruções seguintes a catch. Portanto, é função do trata-
dor de exceções remediar o problema que causou a exceção, para que a execução do
programa possa continuar normalmente.
Lembre-se, se nenhuma exceção for lançada por um bloco try, nenhuma ins-
trução catch será executada e o controle do programa será retomado após a instrução
catch. Para confirmar isso, no programa anterior, mude a linha
nums[7] = 10;

para
nums[0] = 10;

Agora, nenhuma exceção é gerada e o bloco catch não é executado.


É importante entender que as exceções do código que fica dentro de um blo-
co try estão sendo monitoradas. Isso inclui exceções que podem ser geradas por
um método chamado de dentro do bloco try. Uma exceção lançada por um método
chamado de dentro de um bloco try pode ser capturada pelas instruções catch as-
sociadas a esse bloco try – presumindo, claro, que o próprio método não capture a
exceção. Por exemplo, este é um programa válido:
/* Uma exceção pode ser gerada por um
método e capturada por outro. */

class ExcTest {

173
Capítulo 9 Tratamento de exceções

// Gera uma exceção.


static void genException() {
int nums[] = new int[4];

System.out.println("Before exception is generated.");

// gera uma exceção de índice fora do limite


nums[7] = 10; A exceção é gerada aqui.
System.out.println("this won't be displayed");
}
}
A exceção é capturada aqui.
class ExcDemo2 {
public static void main(String args[]) {

try {
ExcTest.genException();
} catch (ArrayIndexOutOfBoundsException exc) {
// captura a exceção
System.out.println("Index out-of-bounds!");
}
System.out.println("After catch statement.");
}
}

Esse programa produz a saída a seguir, que é igual à produzida pela primeira
versão mostrada anteriormente:
Before exception is generated.
Index out-of-bounds!
After catch statement.

Já que genException( ) é chamado de dentro de um bloco try, a exceção que ele


gera (e não captura) é capturada pela instrução catch de main( ). No entanto, é bom
ressaltar que se genException( ) tivesse capturado a exceção, ela nunca teria sido
passada para main( ).

Consequências de uma exceção não capturada


Capturar uma das exceções padrão Java, como fez o programa anterior, tem um be-
nefício adicional: impede que o programa seja encerrado de modo anormal. Quando
uma exceção é lançada, ela deve ser capturada por um código em algum local. Em
geral, quando o programa não captura uma exceção, ela é capturada pela JVM. O
problema é que o tratador de exceções padrão da JVM encerra a execução e exibe
um rastreamento de pilha e uma mensagem de erro. Por exemplo, nesta versão do
exemplo anterior, a exceção de índice fora do limite não é capturada pelo programa.
// Deixa a JVM tratar o erro.
class NotHandled {
public static void main(String args[]) {
int nums[] = new int[4];

174
Java para Iniciantes

System.out.println("Before exception is generated.");

// gera uma exceção de índice fora do limite


nums[7] = 10;
}
}

Quando ocorre o erro de indexação do array, a execução é interrompida, e a


mensagem de erro a seguir é exibida.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7
at NotHandled.main(NotHandled.java:9)

Embora essa mensagem seja útil na depuração, no mínimo não seria algo que
você gostaria que outras pessoas vissem! Por isso, é importante seu programa tratar
ele próprio as exceções, em vez de depender da JVM.
Como mencionado anteriormente, o tipo da exceção deve coincidir com o tipo
especificado em uma instrução catch. Se não coincidir, a exceção não será captura-
da. Por exemplo, o programa abaixo tenta capturar um erro no limite do array com
a instrução catch de uma ArithmeticException (outra das exceções internas Java).
Quando o limite do array é excedido, uma ArrayIndexOutOfBoundsException é
gerada, mas não será capturada pela instrução catch. Isso resulta no programa sendo
encerrado de modo anormal.
// Não funcionará!
class ExcTypeMismatch {
public static void main(String args[]) {
int nums[] = new int[4]; Essa linha lança uma
ArrayIndexOutOfBoundsException.
try {
System.out.println("Before exception is generated.");

//gera uma exceção de índice fora do limite


nums[7] = 10;
System.out.println("this won't be displayed");
}

/* Não pode capturar um erro de limite de


array com uma ArithmeticException. */
Essa linha tenta capturá-la com
catch (ArithmeticException exc) {
uma ArithmeticException.
// captura a exceção
System.out.println("Index out-of-bounds!");
}
System.out.println("After catch statement.");
}
}

A saída é mostrada aqui:


Before exception is generated.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7
at ExcTypeMismatch.main(ExcTypeMismatch.java:10)

175
Capítulo 9 Tratamento de exceções

Como a saída mostra, a instrução catch de uma ArithmeticException não


captura uma ArrayIndexOutOfBoundsException.

Exceções permitem que você trate erros normalmente


Um dos principais benefícios do tratamento de exceções é que ele permite que seu
programa responda a um erro e então continue a ser executado. Por exemplo, con-
sidere o caso a seguir que divide os elementos de um array pelos de outro. Se uma
divisão por zero ocorrer, uma AritmethicException será gerada. No programa, essa
exceção é tratada pelo relato do erro e a execução continua. Logo, tentar dividir por
zero não causa um erro abrupto de tempo de execução resultando no enceramento do
programa. Em vez disso, a situação é tratada normalmente, permitindo que a execu-
ção do programa continue.
// Trata o erro normalmente e continua a execução.
class ExcDemo3 {
public static void main(String args[]) {
int numer[] = { 4, 8, 16, 32, 64, 128 };
int denom[] = { 2, 0, 4, 4, 0, 8 };

for(int i=0; i<numer.length; i++) {


try {
System.out.println(numer[i] + " / " +
denom[i] + " is " +
numer[i]/denom[i]);
}
catch (ArithmeticException exc) {
// captura a exceção
System.out.println("Can't divide by Zero!");
}
}
}
}

A saída do programa é mostrada abaixo:


4 / 2 is 2
Can't divide by Zero!
16 / 4 is 4
32 / 4 is 8
Can't divide by Zero!
128 / 8 is 16

Esse exemplo ilustra outro ponto importante: uma vez que uma exceção foi
tratada, ela é removida do sistema. Portanto, no programa, cada vez que o laço é
percorrido, entramos novamente no bloco try; qualquer exceção anterior terá sido
tratada. Isso permite que seu programa trate erros repetidos.

Usando várias instruções catch


Como mencionado, você pode associar mais de uma instrução catch a uma instrução
try. Na verdade, isso é comum. No entanto, cada catch deve capturar um tipo de

176
Java para Iniciantes

exceção diferente. Por exemplo, o programa mostrado aqui captura erros tanto de
limite de array quanto de divisão por zero:
// Usa várias instruções catch.
class ExcDemo4 {
public static void main(String args[]) {
// Aqui, numer é maior do que denom.
int numer[] = { 4, 8, 16, 32, 64, 128, 256, 512 };
int denom[] = { 2, 0, 4, 4, 0, 8 };

for(int i=0; i<numer.length; i++) {


try {
System.out.println(numer[i] + " / " +
denom[i] + " is " +
numer[i]/denom[i]);
}
catch (ArithmeticException exc) { Várias instruções catch
// captura a exceção
System.out.println("Can't divide by Zero!");
}
catch (ArrayIndexOutOfBoundsException exc) {
// captura a exceção
System.out.println("No matching element found.");
}
}
}
}

Esse programa produz a saída a seguir:


4 / 2 is 2
Can't divide by Zero!
16 / 4 is 4
32 / 4 is 8
Can't divide by Zero!
128 / 8 is 16
No matching element found.
No matching element found.

Como a saída confirma, cada instrução catch responde apenas ao seu tipo de exceção.
Em geral, as expressões catch são verificadas na ordem em que ocorrem em
um programa. Só uma instrução que apresente correspondência é executada. Todos
os outros blocos catch são ignorados.

177
DICA DO PROFESSOR

Assista ao vídeo e veja o uso das instruções try-catch.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Com relação ao tratamento de exceções, marque a alternativa incorreta.

A) Exceções são erros de execução.

B) Erros de tempo de execução não podem ser tratados.

C) O tratamento de exceções automatiza grande parte do código de tratamento de erros.

D) A linguagem de programação java define exceções-padrão para erros que são comuns nos
programas.

E) O tratamento de exceções permite que o programa defina um bloco de códigos chamado


tratador de exceções.

2) O tratamento de exceções em java é gerenciado por cinco palavras-chave. Marque a


alternativa correta.

A) Try, catch, throw, throws e finally.

B) Try, catch, class, final e finally.

C) Try, catch, switch-case, throw e finally.

178
D) Void, throw, throws, static e abstract.

E) Private, public, static, protected e void.

3) Analise o código a seguir e marque a alternativa incorreta.

try {

//bloco de código cujos erros estão sendo monitorados


}
catch(TipoExceç1 obEx){
//tratador de TipoExceç1
}
catch(TipoExceç2 obEx){
//tratador de TipoExceç2
}

A) O código é referente a um tratamento de exceção utilizando try-catch.

B) No código, o try é responsável por monitorar a exceção, que pode ser qualquer
implementação.

C) As palavras try-catch formam a base do tratamento de exceções.

D) No código temos dois catch associados a um único try.

E) Caso uma exceção fosse lançada, ela seria capturada pela instrução try.

4) Quais são as consequências de uma exceção não capturada?

A) O programa apenas ignora a instrução catch.

179
B) O programa terminará normalmente.

C) Não teremos problemas caso o programa passe uma mensagem através de um método print
para o usuário.

D) O programa não terminará de forma normal.

E) Não teremos necessidade de usar o try-catch.

5) Marque a alternativa incorreta.

A) Um dos principais benefícios da exceção é que seu programa não pare a execução.

B) As divisões por zero ocorrem no erro "ArithmeticException".

C) Quando extrapolamos o limite de um Array ocorre um erro


"ArrayIndexOutOfBoundsException".

D) Blocos try podem ser aninhados.

E) Caso tenhamos um try dentro de outro, o interno não necessita ter uma instrução catch
associada.

NA PRÁTICA

Toda aplicação está suscetível a erros, e estes erros podem acontecer com uma frequência mais
comum do que se pensa. Para exemplificarmos erros que podem ocorrer na aplicação, podemos
citar os seguintes: tentar abrir um arquivo inexistente, manipular um tipo de dado como se fosse
outro tipo, fazer uma divisão por zero, manipular um objeto com valor nulo, tentar utilizar
métodos ou classes não existentes, tentar conectar a um servidor ou banco de dados inexistente,

180
entre outros.

Esses erros são chamados de exceções e devem ser tratados pelo programador para controlar tais
imprevistos. Para tratar as exceções em java, são utilizados os comandos try catch. O try catch
funciona da seguinte maneira: o código que você quer monitorar fica dentro do bloco do try;
caso aconteça alguma exceção, o catch captura e trata.

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Aula de Java 037 - try catch, tratamento de erros

Assista e vídeo e saiba mais sobre como tratar possíveis erros em Java.

Conteúdo interativo disponível na plataforma de ensino!

Try, Catch e Exemplos: Tratamento de Erros – Parte 1

No artigo abaixo você saberá mais sobre o tratamento de erros com os blocos Try e Catch, em
C#.

181
Conteúdo interativo disponível na plataforma de ensino!

Blocos Try/Catch

O artigo abaixo artigo apresenta informações sobre programação utilizando os blocos Try/Catch
no Java.

Conteúdo interativo disponível na plataforma de ensino!

182
Introdução à herança

APRESENTAÇÃO

Quando falamos em herança, logo nos vêm à mente situações intrínsecas no meio em que
vivemos; por exemplo, uma pessoa que deixa seus bens como herança para seus dependentes.
Podemos citar também heranças genéticas, um filho herda a cor de pele, a cor de cabelo e a cor
de olhos, entre outras características, de seus pais biológicos. Em orientação a objetos não é
diferente, a não ser o fato de estarmos lidando com códigos. Herança, em orientação a objetos,
consiste na criação de uma estrutura que possui uma classe raiz ou podemos referi-la como
classe pai, mãe, genérica ou superclasse; e classes especializadas, que também podemos chamar
de classes filhas ou subclasses.

Nesta Unidade de Aprendizagem você verá o que é herança e como implementar esta estrutura.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é herança em orientação a objetos.


• Julgar quanto à necessidade da criação de uma estrutura de herança.
• Construir estruturas de herança em uma linguagem de programação.

DESAFIO

Trabalhar com herança em orientação a objetos pode nos trazer muitas vantagens, como a
economia de código; entretanto, é importante termos um bom conhecimento sobre o assunto
para aplicá-lo da melhor forma em nossos projetos.

Você trabalha como analista/programador em uma fábrica de software e foi encarregado de


atender uma locadora de veículos. O cliente necessita realizar o cadastro de sua frota, que é
constituída por veículos de passageiros e de carga.

- Espécie (se o veículo é de carga ou de passageiro), placa, procedência (nacional ou importado),


marca, modelo, ano do modelo, ano da fabricação, chassi, renavam, cor externa, cor interna,
183
combustível, motor, quilometragem, consumo médio, número de portas, número de passageiros
e opcionais.

- Espécie (se o veículo é de carga ou de passageiro), placa, procedência (nacional ou importado),


marca, modelo, ano do modelo, ano da fabricação, chassi, renavam, combustível, motor,
quilometragem, consumo médio, capacidade máxima de carga em kg, altura, largura e
profundidade.

Sua tarefa é implementar as classes de modelo em uma linguagem de programação orientada a


objetos. Considere a possibilidade de criar uma estrutura de herança.

INFOGRÁFICO

Veja, no infográfico, conceitos de herança.

CONTEÚDO DO LIVRO

Trabalhar com herança em orientação a objetos pode nos trazer uma redução significativa de

184
código e, em consequência, um aumento de produtividade. Portanto, o domínio destes conceitos
torna-se vital para o desenvolvimento de softwares.

Boa leitura.

185
Introdução à
herança em C++
Objetivos de aprendizagem
Ao final deste texto, você deve apresentar os seguintes aprendizados:

„„ Definir o que é herança em orientação a objetos.


„„ Identificar a necessidade da criação de uma estrutura de herança.
„„ Construir estruturas de herança em uma linguagem de programação.

Introdução
Ao estudar herança, você percebe seus conceitos, aprende a realizar
reutilização de código (poupando tempo de desenvolvimento), como
utilizar construtores e funções-membro da classe base, bem como a
diferença entre vinculação estática e dinâmica.
Neste capítulo, você estudará um importante conceito de progra-
mação orientada a objetos. Por meio da utilização da herança você será
capaz de definir classes novas que serão extensões de classes existentes.

Conceito
A herança é um dos principais pontos da programação orientada a objetos,
promovendo a extensibilidade do código, a reutilização e uma maior coerência
lógica no modelo de implementação. Os métodos herdados são usados da
mesma forma que os métodos não herdados, nenhum trecho de código é ne-
cessário para mencionar que os métodos serão herdados, o uso do recurso em
uma classe não requer saber se ele foi ou não herdado, o que gera flexibilidade
para o programador.
Entre as vantagens oferecidas pela herança, podemos citar a manutenção
de bibliotecas, para uso futuro de determinados recursos que sejam utilizados
com frequência.

186
Introdução à herança em C++

Segundo Horstmann (2005), a herança é um mecanismo para melhorar classes


existentes, ou seja, se uma classe nova precisa ser implementada e há uma classe
com um conceito geral já disponível, a nova classe poderá herdar da classe existente.
Em C++, uma classe ou estrutura pode ter mais de uma classe base direta
ou estrutura, permitindo especificar se você deseja que uma classe seja uma
classe base simples ou uma classe base virtual. Veja exemplos nas Figuras 1 e 2.

Figura 1. Definição de classe derivada que apresenta o relacionamento entre as classes.


Fonte: Horstmann (2005, p. 386).

Figura 2. Diagrama de herança que apresenta o relacionamento entre as classes.


Fonte: Horstmann (2005, p. 387).

187
Introdução à herança em C++

Necessidade da criação de uma estrutura


de herança
Os dados-membro de uma classe base estão presentes em cada objeto de uma
classe derivada. Porém, não podem ser acessados pelas funções-membro
dessa classe. Como se tratam de dados privativos da classe base, somente ela
tem acesso a eles. Isto mostra que a classe derivada não tem mais direitos de
acesso do que qualquer outra classe.
Quando uma classe é instanciada, o construtor é chamado. Dessa forma, se
ela for derivada de outra, o construtor da classe base é chamado antes. Ainda
dentro desse contexto, se a classe base for derivada de outra, o processo se
repetirá de forma recursiva, até que uma classe não derivada seja atingida,
o que se torna essencial para manter a compatibilidade para o objeto criado.
Quando uma classe base não possuir um construtor sem parâmetros, a
classe derivada tem que declarar um construtor, mesmo que ele esteja vazio.
O construtor de uma classe derivada tem duas funções: a primeira refere-se
a inicializar o objeto base; e a segunda, a inicializar todos os dados-membro.
Observe o modelo da Figura 3.

Figura 3. Construtor com inicializador de classe base.


Fonte: Horstmann (2005, p. 392).

188
Introdução à herança em C++

Caso seja omitido o construtor de uma classe base, o objeto base será
construído com o construtor default dela. Porém, se a classe base não possuir
um construtor default, você terá que chamar, de forma explícita, um construtor
da classe base no construtor da classe derivada. Se você invocar outra função-
-membro sobre o parâmetro implícito, não precisará especificar o parâmetro,
mas somente escrever o nome da função-membro.
Segundo Horstmann (2005), um dos erros mais frequentes é tentar acessar
campos privativos da classe base. Outro possível erro é que, ao estender a
funcionalidade de uma função da classe base, os programadores se esquecem
do nome da classe. Assim, sempre que se chama uma função de determinada
classe base a partir de uma função do mesmo nome de uma classe derivada,
tenha a certeza de fornecer o nome completo da função, incluindo o nome
da classe base.
Ainda dentro da implementação de herança, você precisa saber que existem
tipos de vinculação de objetos, dinâmica e estática. O tipo estático é o tipo
foi declarado no texto do programa como tipo ponteiro, sem fazer qualquer
diferença para onde está apontando; já o tipo dinâmico é determinado pelo
tipo de objeto ao qual se refere neste momento, sendo assim, indica como ele
se comportará. O tipo dinâmico pode ser modificado à medida que o programa
for sendo executado, normalmente por meio de atribuições.
As funções virtuais são dinamicamente vinculadas, portanto, a função espe-
cífica chamada será determinada pelo tipo dinâmico de objeto, de acordo com
o modo como é chamada. Porém, os parâmetros são vinculados estaticamente,
ou seja, você pode chamar uma função definida em uma classe derivada, mas
deve utilizar um valor padrão de parâmetro da classe base.

Nunca redefina um valor padrão de parâmetros herdados, porque os valores


padrão herdados são estaticamente vinculados, enquanto as funções virtu-
ais – as únicas funções que você deve sobrescrever – são dinamicamente
vinculadas (MEYERS, 2011, p. 203).

“Uma classe derivada herda todos os campos de uma classe base. Porém, se os campos
forem privativos, as funções da classe derivada não possuem direito de acesso a eles”
(HORSTMANN, 2005, p. 396).

189
Introdução à herança em C++

Construção de estruturas de herança em


uma linguagem de programação
Quando uma classe herda da outra, membros da classe base são incorporados
como membros da classe derivada. Como as restrições de acessos são geren-
ciadas em classes diferentes, principalmente o acesso aos membros da classe
base a partir das derivadas, você deve aplicar os especificadores de acesso
para obter o controle de acesso à classe base, são eles:

„„ public;
„„ private;
„„ protected.

Herança pública garante que tudo o que se aplica a objetos da classe base,
será aplicado aos objetos da classe derivada, porque cada objeto da classe
derivada é um objeto da classe base. Na classe herdada como public, membros
da classe derivada permanecem como públicos; membros private da classe base
só podem ser acessados por meio de funções públicas ou protegidas da classe
base; e membros protected que estiverem na classe derivada se comportam
na classe derivada como protegidos.
A utilização do atributo protected funciona da mesma forma que o private
sob o ponto de vista interno da classe, a diferença está no fato de que atributos
protected são visíveis pelas classes derivadas, ao passo que os private não.
Essas características podem ser traduzidas por permitir flexibilidade para o
programador.
Na classe herdada como protected, membros public da classe base ficam
como se estivessem protegidos na classe derivada; membros private só podem
ser acessados por funções da classe base que se utilizem das informações que
estão neles; e membros protected se comportam como se fossem copiados
como protegidos na classe derivada.
Na classe herdada como private, membros public agem como se fossem
private na classe derivada, membros private estarão presentes, porém ocultos
como private, só podendo ser acessados por funções da classe base que se utili-
zem das informações que neles contém; já os membros protected comportam-se
como private na classe derivada. Assim, sobressai o atributo mais restritivo.

190
Introdução à herança em C++

Em C++ pode ocorrer de uma classe derivada herdar membros de várias


classes base, o que poderá dar ao programador, maior poder de modelagem,
porém necessitará de mais cuidado na sua utilização, em razão do que chama-
mos de heranças múltiplas. Observe no Quadro 1 uma relação entre heranças
e seus acessos permitidos.

Quadro 1. Tipos de herança considerados no pseudocódigo e acessos permitidos

Acesso a membro Acesso a membro


Tipo de herança
classe base classe derivada

Privado Privado Inacessível


Protegido Privado
Público Privado

Protegido Privado Inacessível


Protegido Protegido
Público Protegido

Público Público Público


Protegido Protegido
Público Público

(Omitido) Público Público


Protegido Protegido
Público Público

Fonte: Adaptado de Aguilar (2008, p. 623).

191
Introdução à herança em C++

Observe a demonstração de herança a seguir.

192
Introdução à herança em C++

193
Introdução à herança em C++

AGUILAR, L. J. Fundamentos de programação: algoritmos, estruturas de dados e objetos.


Porto Alegre: McGraw-Hill, 2008.
HORSTMANN, C. Conceitos de computação com o essencial de C++. 3. ed. Porto Alegre:
Bookman, 2005.
MEYERS, S. C ++ eficaz: 55 maneiras de aprimorar seus programas e projetos. 3. ed.
Porto Alegre: Bookman, 2011.

Leituras recomendadas
VOTRE, V. P. C++ explicado e aplicado. Rio de Janeiro: Alta Books, 2016.
WIKILIVROS. Programar em C++: herança. 2011. Disponível em: <https://pt.wikibooks.
org/wiki/Programar_em_C%2B%2B/Heran%C3%A7a>. Acesso em: 23 abr. 2018.

194
DICA DO PROFESSOR

Assista ao vídeo e veja algumas dicas de quando e como usar herança no paradigma de
programação orientado a objetos.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Na orientação a objetos temos um importante conceito, que é a herança. Marque a


alternativa que melhor define herança em orientação a objetos:

A) Herança refere-se ao comportamento dos objetos.

B) Quando o valor de um atributo pode ser compartilhado entre vários objetos.

C) Quando há a necessidade de utilizar uma classe que está em outro pacote, fazemos uso da
palavra-chave herança.

D) Dizemos que há herança quando duas ou mais classes dependem uma da outra.

E) A herança é um mecanismo para aprimorar as classes existentes.

2) Quando identificamos a possibilidade de usar herança em nosso projeto?

A) Quando temos a necessidade de reduzir código.

B) Quando existe relação entre classes.

195
C) Quando necessitamos aumentar a produtividade no desenvolvimento.

D) Quando temos uma relação e um ou vários atributos em comum entre duas ou mais
classes.

E) Quando classes possuem muitos atributos.

3) Em relação à herança podemos afirmar que:

A) Classes especializadas são aquelas específicas de um determinado domínio.

B) Em uma estrutura de herança, todas as classes necessitam possuir o método main().

C) A herança acontece quando, no desenvolvimento, temos a possibilidade de reuso de


componentes.

D) Em herança possuímos classes genéricas e especializadas.

E) Métodos não podem ser herdados por subclasses.

4) Analise o seguinte código de uma classe em java:

public class Agua extends Bebida {

private String ph;

public String getPh(){ return ph;


}
public void setPh(String ph){
this.ph=ph;
}

196
}

A) O código é inválido pois não possui um método main().

B) Não é necessária a criação de métodos de acesso como get e set, pois a classe possui
apenas um atributo.

C) A palavra-chave "extends" informa que a classe "Agua” é uma extensão da classe


"Bebida".

D) A classe "Agua", quando instanciada, terá acesso apenas ao atributo ph, através dos
métodos set e get.

E) A classe não poderá ser instanciada, pois é necessário que os métodos de acesso da
superclasse estejam implementados na subclasse

5) Analise o código abaixo:

public class Animal {


private int idAnimal;
private String nome;
private int idade;

public String getNome(){

return nome;
}
public void setNome(String nome){

this.nome = nome;
}

public int getIdade(){

197
return idade;
}
public void setIdade(int idade){

this.idade = idade;
}

=================== // ============================

public class Mamifero extends Animal{

private String gestacao;


private String especVida;

public String getGestacao(){

return gestacao;
}
public void setGestacao(String gestacao){

this.gestacao = gestacao;
}

public String getEspecVida(){

return especVida;
}
public void setEspecVida(String especVida){

this.especVida = especVida;
}

198
=============================//==========================

public class Cachorro Mamifero{

private String raca;

public String getRaca(){

return raca;
}
public void setRaca(String raca){

this.raca = raca;
}

============================//============================

public class Homem extends Mamifero{

private String etnia; public String getEtnia(){

return etnia;
}
public void setEtnia(String etnia){

this.etnia = etnia;
}

A) A classe "Animal" é subclasse de "Mamifero".

199
B) A classe "Cachorro" está estendendo de "Mamifero".

C) A sintaxe da classe "Homem" está incorreta.

D) Por se tratar de herança, a classe "Homem" poderá herdar apenas membros da classe
"Mamifero".

E) Na estrutura implementada podemos afirmar que a classe "Homem" é subclasse; a classe


"Mamifero" é subclasse e superclasse e a classe "Animal" é superclasse.

NA PRÁTICA

Assista ao vídeo e veja na prática como implementar uma estrutura de herança na linguagem de
programação java.

Conteúdo interativo disponível na plataforma de ensino!

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Herança em Java

O que é, para que serve, exemplos e quando usar

Conteúdo interativo disponível na plataforma de ensino!

Herança

Nesse vídeo você a aplicação e para que serve, como reaproveitamento de código e
polimorfismo.

200
Conteúdo interativo disponível na plataforma de ensino!

OCA Java SE 8 - Guia de Estudos para o Exame 1Z0-808

Acompanhe o capítulo 7 Herança de Classes,onde verá como implementar e usar herança e os


tipos de classe, seus aspectos básicos, métodos e outros ações que consistem em um dos três
princípios principais de programação.

201
Herança na orientação a objetos

APRESENTAÇÃO

Durante o desenvolvimento de programas em Java, o desenvolvedor costuma criar classes para


representar objetos reais. Por exemplo, para tratar de assuntos relacionados a funcionários em
uma empresa, cria-se uma classe funcionário que contém os atributos de um funcionário, como
nome, CPF e salário, e os métodos que manipulam esses atributos. Imagine uma empresa que
tem diferentes tipos de funcionários, por exemplo, gerente, analista e desenvolvedor. Nesse
caso, cada categoria de funcionário teria atributos específicos. Uma maneira de não repetir os
atributos desnecessários é utilizando o conceito de herança, em que se cria uma classe com os
atributos e os métodos comuns de todos os tipos de funcionários e, então, criar classes
específicas para cada um dos tipos de funcionários. Utilizar herança é uma forma de reutilizar
código em Java.

Nesta Unidade de Aprendizagem, você aprenderá o que é herança na programação orientada a


objetos, a utilizar construtores nesse contexto e a desenvolver uma aplicação em Java.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é herança em orientação a objetos.


• Utilizar sobrecarga de construtor na herança.
• Construir uma aplicação com estrutura de herança fazendo uso de construtores para criar
objetos.

DESAFIO

Saber manipular corretamente classes e suas relações, muitas


vezes, não é uma tarefa trivial. Garantir a integridade dos dados
e das informações manipuladas é essencial para qualquer aplicação
e, para algumas, é um pouco mais crítico.

202
Imagine que você é desenvolvedor Java em uma startup fintech ou financeira. Uma de suas
tarefas é implementar uma funcionalidade
para gerenciar os saques dos clientes correntistas. Nesse contexto,
crie uma classe para representar uma conta corrente, com métodos para depositar uma quantia,
sacar uma quantia e obter o saldo atual
da conta.

O seu analista passou as seguintes regras para a implementação:

- Para cada saque será debitada uma taxa de operação equivalente


a 0,5% do valor sacado.

- Clientes especiais pagam taxas de operação de apenas 0,1% do


valor sacado.

Sendo assim, desenvolva uma solução utilizando herança no Java


para essa situação.

INFOGRÁFICO

Desenvolver código limpo e fácil de manter, muitas vezes, é um desafio para desenvolvedores.
A alta demanda do dia a dia e a necessidade
de aumentar a produtividade pode ser uma armadilha para produzir códigos que futuramente
trarão mais problemas na manutenção, aumentando cada vez mais os custos. Quando se trata de
código
em Java, existem alguns padrões que podem ser seguidos para que o programador não cometa
erros ao abusar de conceitos como herança
e sobrecargas. Quando se aplica herança no código, é possível, muitas vezes, agrupar várias
classes e deixá-las amarradas, o que pode
se tornar um problema ao realizar uma modificação em alguma
das classes.

Um dos padrões para contornar esse problema é o princípio SOLID,


o qual você irá conhecer um pouco mais neste Infográfico.

203
204
CONTEÚDO DO LIVRO

A herança é uma das características mais poderosas e importantes da orientação a objetos, pois
permite o reaproveitamento de atributos e de métodos no código. O reaproveitamento de código
é um dos principais pontos capazes de melhorar a produtividade de um programador, afinal,
reescrever trechos de código sem necessidade é um desperdício de tempo. Na programação
orientada a objetos, o conceito de herança pode ajudar desenvolvedores a produzirem sistemas
mais eficientes e fáceis de manter.

No capítulo Herança na orientação a objetos, da obra Programação orientada a objetos, base


teórica desta Unidade de Aprendizagem, você vai conhecer um pouco mais sobre como funciona
a herança em Java, como sobrescrever métodos e construtores, bem como a fazer uma aplicação
desses conceitos.

Boa leitura.

205
Herança na orientação
a objetos
Objetivos de aprendizagem
Ao final deste texto, você deve apresentar os seguintes aprendizados:

 Definir o que é herança em orientação a objetos.


 Utilizar sobrecarga de construtor na herança.
 Construir uma aplicação com estrutura de herança fazendo uso de
construtores para criar objetos.

Introdução
As linguagens de programação que possuem paradigma orientado
a objetos são amplamente utilizadas, tanto na indústria quanto na
academia. A primeira linguagem de programação totalmente orien-
tada a objetos teve origem na década de 1970, chamada de Smalltalk
(GOLDBERG, 1984). Na década seguinte, começaram a surgir outras
linguagens com o mesmo paradigma, como Delphi, Pascal e Java.
A linguagem Java ganhou destaque e se popularizou, pois fornece,
entre outras coisas, portabilidade.
Nesse contexto de orientação a objetos, alguns conceitos são funda-
mentais, sendo um deles o de herança. O relacionamento de herança é
algo inovador na orientação a objetos e forneceu um meio de abstrair
informações de formas mais precisas.
Neste capítulo, você estudará sobre o princípio de herança em Java
na orientação a objetos. Além disso, verá como utilizar a sobrecarga de
construtores. Por fim, verá como construir uma aplicação com estrutura
de herança utilizando construtores para criar objetos.

206
Herança na orientação a objetos

1 Herança no paradigma orientado a objetos


O paradigma de programação orientado a objetos possui princípios e conceitos
que são comuns a todas as linguagens que seguem esse paradigma, como C++,
PHP e Java. Por isso, compreender os conceitos do paradigma orientado a
objetos é fundamental para que se possa desenvolver em qualquer linguagem
que o siga.
De acordo com Rumbaugh et al. (1991), o paradigma de programação
orientado a objetos é uma maneira de se pensar os problemas da programação
utilizando conceitos do mundo real. Nessa abordagem, o componente funda-
mental é o objeto, que combina estrutura (classe) e comportamento em uma
única entidade. Os principais conceitos da programação orientada a objetos
são: classes, objetos, associação, encapsulamento, polimorfismo e herança.
Juntos, esses princípios compõem a base da lógica e a estrutura de programas
orientados a objetos.
A classe representa uma abstração de quaisquer objetos no mundo real.
Por abstração, entende-se a capacidade de descrever somente as principais
características, sem se ater a detalhes. Por exemplo, pode-se definir uma
classe Sofa para descrever um sofá. Os atributos dessa classe correspondem
às características dos objetos que pertencem a ela. No caso da classe Sofa,
os atributos podem ser número de lugares, cor e material. Observe, a seguir,
o código em Java.

public class Sofa{


private int numeroLugares;
private String cor;
private String material;
}

Agora, imagine um sistema que gerencia o estoque de uma loja de móveis.


Nesse cenário, o sistema deverá manipular informações sobre diferentes tipos
de móveis. Por exemplo, poderíamos criar uma classe para sofá, outra classe
para mesa, mais uma para armário. No entanto, todos esses objetos possuem
algo em comum, são todos móveis. Dessa forma, é possível criar uma classe
Movel para manipular essas informações. Observe, a seguir, o código para
a classe Movel.

207
Herança na orientação a objetos

public class Movel{


private int numeroLugares; //atributo de sofá e mesa
private String cor;//atributo de todos os móveis
private String material;// atributo de todos os móveis
private numeroPortas; //atributo de guarda-roupas e armários
}

Como pode ser visto na Figura 1, nesse caso, tem-se, em uma mesma
classe, muitos atributos que não pertencem ao mesmo objeto. Por exemplo, ao
instanciar um objeto sofa da classe Movel, esse objeto não possui informa-
ções sobre o número de portas. É importante ressaltar que manter atributos e
responsabilidades em um objeto que não pertencem a ele não é uma prática
correta de programação.
Para lidar com esse cenário, pode-se manter na classe Movel o que é
comum a todos os móveis e criar classes específicas para cada um dos móveis
que possuem características diferentes. A Figura 2 apresenta o diagrama de
classes com a estrutura de como se pode modelar para que as classes não
repitam atributos.

Figura 1. Diagrama de classes com relacionamento de herança.

208
Herança na orientação a objetos

Figura 2. Implementação da classe Movel em Java.

Essa relação, em que uma classe possui elementos comuns e as demais


classes derivem dela, é chamada de herança. No diagrama de classes, a di-
reção da seta indica a herança. Nesse caso, as classes Sofa, Armario e
Mesa herdam da classe Movel, ou pode-se dizer que elas são classes-filhas
da classe Movel.
Em uma relação de herança, as classes-filhas possuem acesso aos atributos
e métodos da classe-pai, com exceção dos métodos e atributos indicados como
protected. Para compreender como esse conceito se aplica na programação,
observe a Figura 3, que apresenta o código da implementação das classes
Movel, Sofa, Armario e Mesa.

209
Herança na orientação a objetos

Figura 3. Implementação de classes-filhas da classe Movel


em Java.

A marcação extends na declaração da classe indica a relação de herança.


É possível ler public class Sofa extends Movel(){} da seguinte
forma: a classe Sofa estende a classe Movel. Os objetos da classe-filha
possuem todos os atributos e métodos da classe-pai. Por exemplo, se um objeto
da classe Sofa for criado, ele terá os atributos cor, valor, material e assentos.

210
Herança na orientação a objetos

Ao trabalhar com relacionamentos entre as classes no Java, fique atento ao conceito


de encapsulamento. Existem dois níveis de encapsulamento:
1. Nível de classe: quando o acesso de uma classe inteira é determinado, o qual
pode ser public.
2. Nível de membro (atributo ou método): quando o acesso de atributos ou mé-
todos de uma classe são determinados, os quais podem ser public, private,
protected.
Os modificadores de acesso em Java são os seguintes:
 public: pode ser acessado de qualquer lugar e por qualquer entidade que possa
visualizar a classe a que ele pertence.
 private: não pode ser acessado ou utilizado por nenhuma outra classe. Não se
aplica às classes, somente para seus métodos e atributos. Os atributos e métodos de
uma classe-pai definidos como private não podem ser vistos pelas classes-filhas.
 protected: torna o membro acessível às classes do mesmo pacote ou por meio
de herança.

2 Sobrecarga de construtor
As classes possuem métodos responsáveis por inicializar o objeto, os quais são
chamados de construtores. Em Java, o construtor é definido como um método
cujo nome deve ser o mesmo da classe e sem indicação do tipo de retorno.
O construtor é unicamente invocado no momento da criação do objeto, por
meio do operador new (DEITEL; DEITEL, 2017). O retorno desse operador
é uma referência para o objeto recém-criado.
Em Java, o construtor pode receber argumentos, como em qualquer mé-
todo. Uma classe pode possuir mais de um construtor, o que é chamado de
sobrecarga (DEITEL; DEITEL, 2017). Observe, por exemplo, os construtores
definidos para a classe Point do pacote java.awt. A classe java.awt.
Point tem dois campos, x e y, do tipo int. Essa classe oferece um construtor
que permite criar um ponto representando as suas coordenadas, passadas
como argumentos para o construtor:

public Point(int x, int y)

211
Herança na orientação a objetos

Esse construtor constrói e inicializa um ponto no local especificado (x, y)


no espaço de coordenadas. Além disso, existe também outro construtor que
recebe como argumento um outro objeto da classe Point:

public Point(Point p)

O construtor que recebe um argumento constrói e inicializa um ponto


no mesmo local onde o objeto point foi especificado. Por fim, a classe Point
possui também um construtor-padrão, que não recebe nenhum parâmetro:

public Point()

O construtor-padrão constrói e inicializa um ponto na origem (0, 0) do


espaço de coordenadas. Por padrão, toda classe no Java possui, pelo menos,
um construtor definido. Se nenhum construtor for explicitamente definido
na classe, um construtor-padrão, que não recebe argumentos, é incluído para
a classe pelo compilador Java. No entanto, se algum método construtor for
definido, o construtor-padrão não será criado automaticamente, de modo
que, para utilizá-lo, seria preciso declarar um construtor sem argumentos
explicitamente.
Segundo Deitel e Deitel (2017), no momento em que um construtor é invo-
cado, a seguinte sequência de ações é executada para a criação de um objeto:

 O espaço para o objeto é alocado em memória e seu conteúdo é inicia-


lizado com zeros.
 O construtor da classe-pai é invocado. Se a classe não possui um re-
lacionamento de herança, por padrão, o Java chama o construtor da
classe Object.
 Os membros da classe são inicializados para o objeto, seguindo a ordem
em que foram declarados na classe.
 O restante do corpo do construtor é executado.

Seguir essa sequência é uma necessidade, a fim de garantir que, quando o


corpo de um construtor esteja sendo executado, o objeto já terá à disposição as
funcionalidades mínimas necessárias, quais sejam aquelas definidas por seus
ancestrais. O primeiro passo garante que nenhum campo do objeto terá um
valor arbitrário que possa tornar erros de não inicialização difíceis de detectar.

212
Herança na orientação a objetos

3 Construindo uma aplicação Java com herança


Para melhor compreensão dos conceitos apresentados sobre herança entre
classes, analisaremos um exemplo de como implementar uma aplicação em Java
para gerenciar os funcionários em uma universidade. Essa aplicação deve ser
capaz de realizar o cadastro de alunos, professores e técnicos administrativos.
Nesse contexto, a primeira abstração que se pode fazer é que professores e
técnicos são pessoas e possuem características em comum, como nome, data
de nascimento, CPF e e-mail. Dessa forma, crie uma classe Pessoa para
agrupar os atributos comuns, conforme a seguir:

import java.util.Date;

public class Pessoa {

public String nome;


public String cpf;
public Date data _ nascimento;

//Constructor da classe pessoa


public Pessoa(String _ nome, String _ cpf, Date _ data) {
this.nome = _ nome;
this.cpf = _ cpf;
this.data _ nascimento = _ data;
}

A classe Pessoa possui os atributos nome, cpf e data_nascimento


e um construtor, que inicializa esses valores. Agora, deve-se implementar as
classes para professor e técnico administrativo. Contudo, observe que esses
dois também possuem algo em comum pelo qual podem ser agrupados: ambos
são funcionários. Dessa forma, crie uma classe Funcionario:

import java.util.Date;

//Funcionario é classe herdeira da classe Pessoa


public class Funcionario extends Pessoa{

private double salario;

213
Herança na orientação a objetos

public Funcionario(String _ nome, String _ cpf, Date _ data) {


super( _ nome, _ cpf, _ data); //chamada do constructor pai
}

//calcula salario do funcionário


public double calculaSalario(int horasTrabalhadas) {
return horasTrabalhadas*10;
}

A classe Funcionario estende, ou herda, a classe Pessoa. A classe


Funcionario possui o atributo salario, bem como um método para
calcular esse atributo de acordo com a quantidade de horas trabalhadas por
cada funcionário. Além disso, observe que o construtor da classe Fun-
cionario possui a chamada super, que indica que está sendo cha-
mado o método construtor da classe-pai. Ou seja, quando se instancia
um objeto da classe Funcionario, é feita uma chamada para o cons-
trutor da classe Pessoa. Agora, escreva as classes para Professor e
TecnicoAdministrativo:

import java.util.Date;

public class Professor extends Funcionario {

public Professor(String _ nome, String _ cpf, Date _ data) {


super( _ nome, _ cpf, _ data);
}

public class TecnicoAdministrativo extends Funcionario{

public TecnicoAdministrativo(String _ nome, String _ cpf,


Date _ data) {
super( _ nome, _ cpf, _ data);
}
}

214
Herança na orientação a objetos

Ambas as classes, Professor e TecnicoAdministrativo, herdam


a classe Funcionario, que, por sua vez, também herda a classe Pessoa.
Dessa forma, um objeto professor possui todos os atributos e métodos das
classes Pessoa e Funcionario. Observe, a seguir, como fica a instanciação
de um objeto da classe Professor.

Professor prof = new Professor(“Paulo”, “622.875.720-23”, new


Date());

//Calcula salario da classe Funcionario


System.out.println(prof.calculaSalario(400));

//Atributos Nome e CPF da classe Pessoa


System.out.println(prof.getNome));
System.out.println(prof.getCpf));

Ambas as classes, Professor e TecnicoAdministrativo, pos-


suem o atributo salario, que está na classe Funcionario. No entanto,
o cálculo do salário do professor é diferente do cálculo do salário do técnico.
Para explicitar essa diferença, pode-se sobrescrever na classe Professor
o método calculaSalario, passando, além da quantidade de horas, a
classe de contratação.

public class Professor extends Funcionario {

public Professor(String _ nome, String _ cpf, Date _ data) {


super( _ nome, _ cpf, _ data);
}

//sobrescreve o calculo de salario do funcionário


public double calculaSalario(int horasTrabalhadas, double
bonificação) {
return super.calculaSalario(horasTrabalhadas)*bonificacao;
}

215
Herança na orientação a objetos

Agora, quando o método para calcular o salário do objeto professor


for chamado, ele executará o método calculaSalario de acordo com a
quantidade de parâmetros passados a ele. Se ele receber dois parâmetros, será
chamado o método da classe Professor; se receber apenas um parâmetro,
então será chamado o método calculaSalario da classe Funcionario.
Observe como fica a instanciação dos objetos:

Professor prof = new Professor(“Paulo”, “622.875.720-53”, new


Date());
TecnicoAdministrativo tecnico = new TecnicoAdministrativo(“João”,
“457.414.970-12”, new Date());

//Calcula salario da classe Professor


System.out.println(“Salario do professor: “+prof.calculaSala-
rio(60 , 1.5));

//Calcula salario da Classe Funcionario


Syste m.out.println(“Salario do Técnico: “+tecnico.
calculaSalario(60));

DEITEL, P.; DEITEL, H. Java: como programar. 10. ed. São Paulo: Pearson Education do
Brasil, 2017.
GOLDBERG, A. Smalltalk-80: the interactive programming environment. Boston: Addison-
-Wesley, 1984.
RUMBAUGH, J. et al. Object-oriented modeling and design. Englewood Cliffs: Prentice-
-Hall, 1991.

216
DICA DO PROFESSOR

Quando se fala de herança, existem muitos conceitos do paradigma de programação orientada a


objetos envolvidos. Por exemplo, se diz que uma classe filha é uma classe especializada da
classe pai, e que a classe pai é uma generalização.

Nesta Dica Do Professor, você vai ver um pouco mais sobre esses conceitos importantes
relacionados à herança na programação orientada a objetos.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Um dos principais conceitos da orientação a objetos é o encapsulamento de dados. O


encapsulamento em Java é
realizado por meio de modificadores de acesso.

Assinale a alternativa que apresenta o modificador que, em uma herança, permite


somente as classes filhas de acessar um atributo
da classe pai.

A) Modificador private.

B) Modificador protected.

C) Modificador public.

D) Modificador default.

E) Modificador extends.

217
2) Em uma classe, temos construtores que são usados para inicializar a classe. Quando,
em uma relação de herança, tanto a superclasse quanto a classe filha têm
construtores, os dois devem ser executados.

Assinale a alternativa que indica como isso ocorre em Java.

A) Para executar o método construtor da superclasse, deve-se aplicar a palavra-chave extends.


Para isso, basta chamar extends na classe filha e passar uma lista de parâmetros que o
construtor da superclasse espera receber.

B) Para executar o método construtor da superclasse, deve-se aplicar a palavra- chave


implements. Para isso, basta chamar implements na classe filha e passar uma lista de
parâmetros que o construtor da superclasse espera receber.

C) Para executar o método construtor da superclasse, deve-se aplicar a palavra-chave super.


Para isso, basta chamar super na classe filha e passar uma lista de parâmetros que o
construtor da superclasse espera receber.

D) Para executar o método construtor da superclasse, deve-se aplicar a palavra-chave abstract


. Para isso, basta chamar abstract na classe filha e passar uma lista de parâmetros que o
construtor da superclasse espera receber.

E) Para executar o método construtor da superclasse, deve-se reescrevê-lo na classe filha.


Para isso, basta chamar extends na classe filha e passar uma lista de parâmetros que o
construtor da superclasse espera receber.

3) O método construtor de uma classe Java é um método especial, que tem o mesmo
nome da classe e é executado quando a classe é instanciada. Em Java, é possível
realizar sobrecarga de construtor.

Assinale a alternativa que apresenta a definição correta de sobrecarga de construtor.

A) É possível criar vários métodos construtores, todos com os mesmos parâmetros.

218
B) É possível criar vários métodos construtores, todos com parâmetros diferentes.

C) É possível criar somente um método construtor e estender esse método por meio de
herança.

D) É possível criar vários métodos construtores encapsulando os atributos no método.

E) É possível criar vários métodos construtores somente em superclasses.

4) Ao aplicar o conceito de herança em Java, aplica-se em todos os métodos, inclusive os


métodos construtores da classe.

Nesse contexto, analise o seguinte código em Java:


Conteúdo interativo disponível na plataforma de ensino!

Assinale a alternativa que indica a saída ao instanciar um objeto da classe horista.

A) Ao se criar um objeto da classe horista será exibida a mensagem "Novo funcionário".

B) Ao se criar um objeto da classe horista será exibida a mensagem "Paulo".

C) Ao se criar um objeto da classe horista não será exibida nenhuma mensagem.

D) Ao se criar um objeto da classe horista será exibida a mensagem "Novo funcionário


Paulo".

E) Ao se criar um objeto da classe horista ocorrerá um erro, pois essa classe horista não tem
construtor.

5) A sobreescrita de métodos é um dos pilares da programação orientada a objetos.


Além de herdar os atributos e os métodos da superclasse, a classe herdeira pode

219
adaptar os métodos herdados.

Analise o código Java a seguir:


Conteúdo interativo disponível na plataforma de ensino!
Assinale a alternativa que indica o resultado da operação.
Conteúdo interativo disponível na plataforma de ensino!

A) O resultado será 600.0, pois o método getBonificacao é sobrescrito na classe gerente.

B) O resultado será 400.0, pois a classe gerente herda da classe funcionário e será executado o
método da superclasse.

C) O resultado será 1000.0, pois serão executados os métodos da classe gerente e funcionário.

D) O resultado será um erro de execução, pois a classe gerente não tem um atributo salário
para o cálculo.

E) O resultado será 400.0, pois a classe gerente sobrecarrega a classe funcionário e será
executado o método da superclasse.

NA PRÁTICA

Muitas vezes, no desenvolvimento de um projeto em Java,


o programador se depara com funcionalidades semelhantes,
mas com circunstâncias especiais. Nesse caso, o programador
pode usar o conceito de herança e sobrecarga de construtores
na classe pai para fazer com que as classes filhas compartilhem
alguns atributos e métodos.

220
Neste Na Prática, você vai ver como Mariana usa sobrecarga de construtores no seu dia a dia no
desenvolvimento de sistemas Java.

221
222
SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Um estudo quantitativo sobre o uso de herança e interface em sistemas Java

O conceito de herança é uma das principais características da programação orientada a objetos.


Por esse motivo, também é amplamente utilizada por programadores. No entanto, existem
restrições ao uso exagerado de heranças entre classes. Neste estudo, o autor avalia o uso de
herança e interface em 1.656 sistemas de código aberto escritos em Java.

Conteúdo interativo disponível na plataforma de ensino!

Padrões de projeto em Java: um estudo prático sobre a utilização e benefícios

O principal objetivo de padrões de projeto é propor soluções reutilizáveis para problemas


recorrentes. Grande parte dos sistemas orientados a objetos utilizam padrões de projeto. Muitas
vezes, desenvolvedores juniores acabam não compreendendo a maneira como a herança é
utilizada nos sistemas que começam a trabalhar. Em grande parte, isso ocorre porque eles não
entendem os padrões de projeto. No trabalho apresentado a seguir, os autores descrevem como
padrões de projetos são utilizados em sistemas orientados a objetos e apresentam boas práticas e
vantagens do uso desses padrões.

Conteúdo interativo disponível na plataforma de ensino!

Uma linguagem de programação quântica orientada a objetos baseada no featherweight


Java

A computação quântica vem sendo estudada e desenvolvida nas últimas décadas como uma
solução às barreiras da computação clássica. Paralelamente ao desenvolvimento de hardware,
existem diversas pesquisas para evoluir as abordagens de software. Nesse contexto, o trabalho a
seguir apresenta a proposta de uma linguagem de programação para computadores quânticos,
baseada no Java e em suas características, como herança e polimorfismo.

Conteúdo interativo disponível na plataforma de ensino!

223
Programação orientada a objetos:
herança e polimorfismo

APRESENTAÇÃO

A reusabilidade de código é uma das grandes vantagens do desenvolvimento de softwares em


linguagens de programação orientada a objetos. Pela estrutura de classes e construção de
programas, pensando em abstrair entidades da realidade para o conceito de objetos, é possível
que os códigos se tornem mais organizados e reutilizáveis.
Quando se pensa em reusabilidade de código em orientação a objetos, é necessário conhecer
como funciona o conceito de herança e polimorfismo.

Nesta Unidade de Aprendizagem, você conhecerá o conceito e tipos de herança e polimorfismo


na visão da orientação a objetos e também como são aplicados, utilizando exemplos em
linguagem de programação orientada a objetos.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Especificar a herança na orientação a objetos.


• Discutir o polimorfismo na orientação a objetos.
• Aplicar a herança e o polimorfismo.

DESAFIO

A herança é um conceito da programação orientado a objetos que permitem o reuso de código.


Por meio do conceito de herança, novas classes são criadas a partir de classes já existentes,
absorvendo os seus atributos e métodos e adicionando os seus próprios.

A partir disso, considere a seguinte situação:

Conteúdo interativo disponível na plataforma de ensino!

224
Com base em tais informações e utilizando o conceito de herança, apresente o modelo de classes
e o modelo descritivo com os atributos e métodos das classes necessárias para atender a esse
requisito. Utilize os métodos 'get' e 'set' para manipulação dos atributos de cada classe.

INFOGRÁFICO

O conceito de herança é um dos conceitos base da programação orientada a objetos. Qualquer


linguagem de programação que implemente o paradigma de orientação a objetos deve permitir
que a herança seja implementada.

Neste Infográfico, você verá a sintetização de conceitos relacionados à herança entre classes,
abordando a hierarquia de classes, herança simples e herança múltipla.

225
226
CONTEÚDO DO LIVRO

A programação orientada a objetos, sem dúvida, possibilitou que os programas de computadores


ficassem mais próximos da realidade. Avanços relacionados à criação de programas em
linguagens orientadas a objetos vão desde a facilidade de acoplação até mesmo a manutenção de
sistemas.

Outro benefício importante da programação orientada a objetos está ligado ao reuso de código.
E, quando se fala em reuso de código em programação orientada a objetos, é necessário falar
sobre herança e polimorfismo.

No capítulo Programação orientada a objetos: herança e polimorfismo, da obra Paradigmas de


programação, base teórica desta Unidade de Aprendizagem, você vai conhecer mais sobre esses
conceitos da programação orientada a objetos, entender os tipos de herança e as formas de
implementação de polimorfismo em linguagens orientadas a objetos.

Boa leitura.

227
Programação orientada
a objetos: herança
e polimorfismo
Objetivos de aprendizagem
Ao final deste texto, você deverá apresentar os seguintes aprendizados:

„„ Especificar a herança na orientação a objetos.


„„ Discutir o polimorfismo na orientação a objetos.
„„ Aplicar a herança e o polimorfismo.

Introdução
O paradigma da programação orientada a objetos surgiu com o intuito
de aplicar conceitos da realidade ao contexto da programação de com-
putadores. O próprio nome, orientação a objetos, remete ao conceito
de que o foco da construção dos programas está em orientar a estrutura
para as entidades objetos, que emulam noções existentes no mundo real.
Para que o paradigma de orientação a objetos tenha maior abrangên-
cia, faz-se necessário que outros conceitos também sejam possíveis e,
nesse escopo, ocorre a herança entre objetos. Assim como na realidade,
no contexto da programação orientada a objetos, a herança está asso-
ciada à característica de possuir comportamentos de outra entidade.
Neste capítulo, iremos entender o funcionamento do conceito de
herança na programação orientada a objetos. Além disso, abordaremos
o conceito de polimorfismo, que permite que a herança entre objetos
seja possível, mesmo com alterações no comportamento herdado, para
atender a características específicas do objeto herdeiro.

228
Programação orientada a objetos: herança e polimorfismo

O conceito de herança na orientação a objetos


O conceito de herança na programação orientada a objetos é utilizado para
se permitir a reutilização de um código. A herança possibilita que classes
compartilhem seus atributos e métodos entre si. Segundo Tucker e Noonan
(2009, p. 335),

[...] o paradigma orientado a objetos suporta reutilização de código por in-


termédio da herança. As classes existem em uma linguagem orientada a
objetos em uma hierarquia de classes. Uma classe pode ser declarada como
uma subclasse de outra classe, que é chamada de classe mãe ou superclasse.

Dentro dessa relação de hierarquia de classes é possível que a subclasse


herde atributos e comportamentos da superclasse, simplesmente pelo fato de
ser sua subordinada.
Na programação orientada a objetos, a relação de herança entre classes é
a relação em que uma classe é do tipo é uma, e não do tipo tem uma. Esta é
uma das confusões recorrentes na construção de programas em orientação
a objetos.
Para ilustrar essa diferença, observe a Figura 1 e veja que, neste exemplo,
temos um tipo de relação é uma, pois o objeto da classe Cachorro, assim
como o objeto da classe Gato, é, por herança, um tipo de objeto da classe
Animal.

Animal

Cachorro Gato

Figura 1. Representação de herança


entre classes.

229
Programação orientada a objetos: herança e polimorfismo

Compare, agora, a relação apresentada na Figura 2, que representa uma


relação do tipo tem uma. Perceba que, neste caso, uma relação de herança
entre as classes Estado e Cidade não faz sentido, visto que um estado
possui cidades, mas uma cidade não é um estado.

Estado Cidade

Figura 2. Representação da relação do tipo tem uma entre


classes.

No conceito de herança, a superclasse geralmente é uma classe genérica,


que engloba os atributos e métodos que podem ser utilizados por qualquer
classe herdeira. Não faz sentido utilizar a superclasse para atribuir, por exem-
plo, um atributo ou método que seja específico de uma subclasse, pois, neste
caso, todas as demais subclasses estariam herdando este atributo ou método
desnecessariamente.
A herança pode apresentar duas formas diferentes. A mais comum e uti-
lizada pelas linguagens de programação, em geral, é a herança simples. No
entanto, algumas linguagens de programação orientada a objetos, como a
linguagem C++ e Python, possibilitam a implementação da herança múltipla
(TUCKER; NOONAN, 2009).
Na herança simples, uma hierarquia de classe forma uma árvore, com
sua raiz na classe genérica. Uma classe D é uma subclasse de outra classe
C quando ela estende ou especializa o significado da classe C e acrescenta
novas variáveis de instância ou métodos, ou quando modifica as definições
dos métodos público e protegido de C.
A herança múltipla, conforme já mencionado, é um recurso que também
pode ser implementado por algumas linguagens de programação orientada
a objetos. A linguagem Java, por exemplo, uma das linguagens orientada a
objetos mais utilizadas não permite a implementação da herança múltipla.
Em contraposição à herança simples, na herança múltipla uma classe pode
herdar atributos e métodos de mais de uma superclasse, atribuindo a esta
comportamentos de diferentes classes.
230
Programação orientada a objetos: herança e polimorfismo

Existem situações em que seu uso pode ser pertinente, mas a herança múl-
tipla também possui desvantagens, especialmente em razão de sua semântica,
que pode dificultar a manutenção do código. Conforme Lima (2014, p. 148)
observa,

[...] uma desvantagem da herança múltipla é que sua semântica se torna muito
complicada em certas circunstâncias. Por exemplo, se uma classe E tem sub-
classes B e C, e um método M é definido diferentemente em B e C, que imple-
mentação de M deveria ser herdada por E: aquela em B, aquela em C ou ambas?

Em alguns casos é necessário que a subclasse possua um comportamento


diferenciado do que foi herdado da superclasse. Na orientação a objetos, cha-
mamos esse conceito de polimorfismo. Na próxima seção, iremos abordá-lo
no contexto da orientação a objetos.

Polimorfismo
O polimorfismo na programação orientada a objetos permite que uma ou mais
classes derivadas de uma mesma superclasse possam invocar métodos que
possuam uma mesma assinatura, mas com comportamentos diferenciados
para cada classe derivada, utilizando, para isso, uma referência a um objeto
da superclasse.
A definição de polimorfismo é mais um dos recursos da orientação a
objetos que possibilita que um comportamento encontrado na realidade seja
aplicado à programação. Na natureza, existem animais que são capazes de
modificar sua forma ou comportamento para atender a determinada situação,
e é isto que o polimorfismo possibilita na programação orientada a objetos.
Segundo Tucker e Noonan (2009, p. 323), “em linguagens orientadas a ob-
jetos, polimorfismo refere-se à ligação tardia de uma chamada a uma ou várias
diferentes implementações de um método em uma hierarquia de herança”.
Para entendermos melhor este exemplo, suponhamos que uma aplicação
implementa um programa de desenho. Em um programa desses, podemos ter
diferentes formas geométricas: círculo, quadrado, retângulo etc.

231
Programação orientada a objetos: herança e polimorfismo

Cada uma das formas geométricas é representada, respectivamente, por


uma das classes apontadas a seguir.

„„ classe circulo;
„„ classe quadrado;
„„ classe retangulo.

Todas essas classes são subclasses da classe FormaGeometrica. Temos,


na superclasse FormaGeometrica, a definição do método desenhar,
mas sabemos que desenhar um círculo é diferente de desenhar um retângulo.
Neste caso, a forma de implementar o método desenhar na subclasse
Circulo deve possuir um comportamento diferente da implementação
na subclasse Retangulo, apesar de todas herdarem e necessitarem dessa
implementação. Portanto, o conceito de polimorfismo serve justamente para
resolver questões como esta.
A Figura 3 ilustra um diagrama de classes com base nesta implementação.

FormaGeometrica

desenhar()

Circulo Retangulo
Quadrado

desenhar() desenhar() desenhar()

Figura 3. Representação de polimorfismo entre classes.

232
Programação orientada a objetos: herança e polimorfismo

Para Lima (2014), são dois os tipos mais recorrentes de polimorfismo na


programação orientada a objetos:

a) polimorfismo estático ou sobrecarga de método;


b) polimorfismo dinâmico ou sobrescrita de método.

O polimorfismo estático ou sobrecarga de método é a forma de implemen-


tação em que são definidos vários métodos com o mesmo nome, mas com
assinaturas diferentes. Ou seja, cada método pode receber diferentes parâmetros
ou, então, os mesmos parâmetros de tipos diferentes. A sobrecarga consiste
em permitir, dentro da mesma classe, mais de um método com o mesmo nome.
Entretanto, eles devem necessariamente possuir argumentos diferentes para
funcionar. Booch, Rumbaugh e Jacobson (2006) afirmam que a escolha de
qual método irá ser chamado pelo programa principal dependerá do seu tipo
de objeto, e esta decisão será tomada apenas no tempo de execução, por meio
de ligação tardia.
Veja o exemplo a seguir de um código em linguagem Java para uma classe
Calculadora. Perceba que o método calcula aparece três vezes: o primeiro
recebe como parâmetro dois valores int, o segundo recebe dois valores
double e o terceiro recebe dois valores String.

public class Calculadora {


public int calcula(int a, int b) {
return a+b;
}
public double calcula(double a, double b) {
return a+b;
}
public String calcula(String a, String b) {
return a+b;
}
}

O polimorfismo dinâmico ou sobrescrita de método nos permite reescrever


um método, ou seja, podemos reescrever nas subclasses os métodos criados
inicialmente na superclasse. Os métodos que serão sobrepostos, diferentemente
dos sobrecarregados, devem possuir o mesmo nome, tipo de retorno e quan-
tidade de parâmetros do método inicial. No entanto, este será implementado

233
Programação orientada a objetos: herança e polimorfismo

com especificações da classe atual, podendo adicionar algo a mais ou não


(LIMA, 2014).
Para ilustrar este exemplo, vamos novamente utilizar um trecho de código
de uma classe em linguagem Java. Primeiramente, observe a escrita do método
setVelocidade na superclasse Veiculo:

public abstract class Veiculo {


public float velocidade;
public void setVelocidade(float v) {
velocidade = v;
}
}

Veja, então, como ficaria a implementação na subclasse Carro. Observe


que a assinatura do método é a mesma da superclasse, ou seja, recebe v como
parâmetro do tipo float. Porém, na subclasse, além de atribuir o valor de v
para velocidade, são feitos um tratamento e uma atribuição de valor ao atributo
marcha, dependendo da velocidade do veículo.

public abstract class Veiculo {


public float velocidade;
public void setVelocidade(float v) {
velocidade=v;
if (velocidade < 20){
marcha = 1;
} else if (velocidade >= 20 && velocidade < 40) {
marcha = 2;
} else if (velocidade >= 40 && velocidade < 60){
marcha = 3;
} else if (velocidade >= 60 && velocidade < 70){
marcha = 4;
} else if (velocidade >= 70){
marcha = 5;
}
}
}

234
Programação orientada a objetos: herança e polimorfismo

Na próxima seção, iremos abordar um exemplo de aplicação de herança e


polimorfismo em linguagens orientadas a objetos.

Aplicação de herança e polimorfismo


Nas seções anteriores, abordamos os conceitos de herança e polimorfismo em
programação orientada a objetos, utilizando a linguagem de programação Java.
Nesta seção, iremos apresentar a implementação destes conceitos na constru-
ção de um programa para uma escola, no qual são utilizados os exemplos de
herança entre as classes Professor e Aluno, que herdam de Pessoa, e o
polimorfismo dinâmico, para o método obterDescontoMensalidade,
que calcula o valor da mensalidade com desconto. A Figura 4 ilustra o modelo
de classes para esta situação.

Pessoa
nome: string
cpf: string
data_nascimento : Date
newAttr : integer

obterDescontoMensalidade(valor)

Professor
Aluno
salario : double
Matricula : String
disciplina : String

Figura 4. Modelo de classes para situação proposta de um programa para uma escola.

235
Programação orientada a objetos: herança e polimorfismo

Primeiramente, vamos ver como ficaria a construção da superclasse


Pessoa.

public class Pessoa {


public String nome;
public String cpf;
public Date data _ nascimento;
public Pessoa (String _ nome, String _ cpf, Date _ data) {
this.nome = _ nome;
this.cpf = _ cpf;
this.data _ nascimento = _ data;
}
}

Após a criação da superclasse, vamos verificar os códigos utilizado para


a criação das subclasses Aluno e Professor. Certifique-se de que sua
declaração inclua as palavras extends Pessoa depois de Aluno. Essa
sintaxe é da linguagem Java e significa que as subclasses devem herdar da
superclasse Pessoa.

public class Aluno extends Pessoa {


public Aluno (String _ nome, String _ cpf, Date _ data) {
super ( _ nome, _ cpf, _ data);
}
public String matricula;
}

public class Professor extends Pessoa {


public Professor (String _ nome, String _ cpf, Date _ data) {
super ( _ nome, _ cpf, _ data);
}
public double salario;
public String disciplina;
}

236
Programação orientada a objetos: herança e polimorfismo

Podemos verificar que, tanto na subclasse Aluno, quanto na subclasse


Professor, o método construtor está utilizando os atributos que foram
declarados na superclasse Pessoa:

_ nome
_ cpf
_ data

Além disso, observe que no caso da subclasse Aluno é definido um atributo


específico matricula, que só faz sentido para Aluno. No caso da subclasse
Professor, os atributos salario e disciplina só fazem sentido no
contexto de Professor.
Estes exemplos ilustram o típico uso de herança, sem haver necessidade
de reescrever nas subclasses o que é comum a elas, escrevendo somente o
que lhes é específico.
Agora, suponhamos que a escola resolveu criar um programa de oferta
de descontos em mensalidades para Aluno e para Professor. Contudo, a
diferença de percentual seria de 20% para os professores e apenas 10% para
alunos. Vamos adotar o desconto de 10% como o desconto comum, logo, seria
criado na superclasse Pessoa o método obterDescontoMensalidade
para retornar o valor do desconto. Dessa forma, não é necessário fazer nenhuma
alteração na subclasse Aluno, pois o comportamento será automaticamente
herdado da superclasse Pessoa.

public class Pessoa {


public String nome;
public String cpf;
public Date data _ nascimento;
public Pessoa (String _ nome, String _ cpf, Date _ data) {
this.nome = _ nome;
this.cpf = _ cpf;
this.data _ nascimento = _ data;
}
public double obterDescontoMensalidade (double valor) {
//Retorna o valor do desconto na mensalidade
return 0.10 * valor;
}
}

237
Programação orientada a objetos: herança e polimorfismo

Como especificado na regra de negócio, os objetos da classe Professor


devem receber um desconto diferenciado de 20%. Logo, utilizando o conceito
de polimorfismo dinâmico, em que o método possui a mesma assinatura,
apenas implementando um novo comportamento, vamos fazer um ajuste para
atender a esta necessidade.
Veja, então, como ficaria a subclasse Professor:

public class Professor extends Pessoa {


public Professor (String _ nome, String _ cpf, Date _ data) {
super ( _ nome, _ cpf, _ data);
}
public double salario;
public String disciplina;
public double obterDescontoMensalidade (double valor) {
//Retorna o valor do desconto na mensalidade
return 0.20 * valor;
}
}

Este foi um exemplo de aplicação simples da utilização em uma mesma


estrutura de classes dos conceitos apresentados neste capítulo. Por fim, é
possível verificar que herança e polimorfismo se relacionam no contexto das
linguagens de programação orientada a objetos.

Acesse o link a seguir para um exemplo de polimorfismo e herança em Python, uma


importante linguagem de programação que permite a orientação a objetos.

https://qrgo.page.link/6xYCs

238
Programação orientada a objetos: herança e polimorfismo

BOOCH, G.; RUMBAUGH, J.; JACOBSON, I. UML: guia do usuário. 2. ed. Rio de Janeiro:
Elsevier; Campus, 2006. 474 p.
LIMA, A. S. UML 2.5: do requisito à solução. São Paulo: Érica, 2014. 368 p.
TUCKER, A. B.; NOONAN, R. E. Linguagens de programação: princípios e paradigmas. 2.
ed. Porto Alegre: AMGH, 2009. 630 p.

Leituras recomendadas
EDELWEISS, N.; LIVI, M. A. C. Algoritmos e programação: com exemplos em Pascal e C.
Porto Alegre: Bookman, 2014. 476 p. (Série Livros Didáticos Informática UFRGS).
LEDUR, C. L. Desenvolvimento de sistemas com C#. Porto Alegre: SAGAH, 2018. 268 p.
MACHADO, R. P.; FRANCO, M. H. I.; BERTAGNOLLI, S. C. Desenvolvimento de software III:
programação de sistemas web orientada a objetos em Java. Porto Alegre: Bookman,
2016. 220 p. (Série Tekne; Eixo Informação e Comunicação).
OKUYAMA, F. Y.; MILETTO, E. M.; NICOLAO, M. Desenvolvimento de software I: conceitos bá-
sicos. Porto Alegre: Bookman, 2014. 236 p. (Série Tekne; Eixo Informação e Comunicação).
NICOLETTI, M. C. A cartilha Prolog. São Carlos: Edufscar, 2003. 124 p. (Série Apontamentos).
PINHEIRO, F. A. C. Elementos de programação em C: em conformidade com o padrão
ISO / IEC 9899. Porto Alegre: Bookman, 2012. 548 p.
SEBESTA, R. W. Conceitos de linguagem de programação. 11. ed. Porto Alegre: Bookman,
2018. 758 p.

239
DICA DO PROFESSOR

Existem dois tipos básicos de implementação do conceito de polimorfismo em linguagens


orientadas a objetos. Pode ser usado o formato de implementação estática ou sobrecarga de
métodos ou então o formato de implementação dinâmica ou sobrescrita de método. Qualquer um
dos formatos é encontrado.

Nesta Dica do Professor, você irá entender o conceito de polimorfismo para que seja possível
diferenciar cada um dos seus tipos de implementação.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Ao analisar um modelo de classes de um sistema, você identificou que existe um


relacionamento de herança entre as classes MeioPagamento e CartaoCredito, sendo a
classe mãe a MeioPagamento e a classe herdeira a CartaoCredito. Sobre essa
situação, assinale a alternativa correta:

A) Somente atributos public da classe MeioPagamento serão herdados pela classe


CartaoCredito.

B) Todos os atributos da classe MeioPagamento serão herdados pela classe CartaoCredito.

C) Todos os atributos serão herdados da classe MeioPagamento, mas os métodos não serão.

D) Todos os atributos serão herdados, mas novos atributos inseridos na classe


MeioPagamento não serão.

E) Não será possível criar um objeto a partir da classe CartaoCredito sem antes criar objeto
MeioPagamento.
240
2) Utilizando ainda o exemplo do exercício anterior, caso existisse a necessidade de
inserir um atributo para armazenar o valor do limite de gastos diários, que seria
único por CartaoCredito, lembrando que a classe MeioPagamento também tem como
herdeira a classe CartaoDebito, assinale a alternativa correta sobre essa
implementação:

A) O atributo limiteGastosDiario seria criado somente na classe CartaoCredito.

B) O atributo limiteGastosDiario seria criado na classe CartaoCredito e MeioPagamento.

C) O atributo limiteGastosDiario seria criado somente na classe MeioPagamento

D) O atributo limiteGastosDiario seria criado na classe MeioPagamento, CartaoCredito e


CartaoDebito.

E) O atributo limiteGastosDiario seria criado na classe MeioPagamento e CartaoCredito, mas


os métodos get e set somente na MeioPagamento.

3) Em algumas linguagens de programação orientada a objetos, como C++ e Python, é


possível que uma classe possua relacionamento de herança com mais de uma classe.
Assinale a alternativa correta quanto ao nome desse tipo de herança:

A) Herança Composta.

B) Herança Associativa.

C) Herança Dupla.

D) Herança Múltipla.

241
E) Herança Binária.

4) Em herança entre classes, existe a possibilidade de uma classe herdeira reescrever


um método implementado pela classe mãe. Dessa forma, a classe herdeira atribui um
comportamento diferenciado ao da classe mãe, sendo o nome desse conceito
polimorfismo. Assinale a alternativa correta sobre o polimorfismo:

A) Só é possível aplicar polimorfismo quando os métodos têm a mesma assinatura.

B) O polimorfismo é um recurso mais recente que a herança na orientação a objetos.

C) Em herança múltipla, não é possível aplicar o conceito de polimorfismo, apenas em


herança simples.

D) O polimorfismo só permite a alteração de escopo de um método da superclasse pela


subclasse.

E) O polimorfismo é uma característica em orientação a objetos que usa a hierarquia de


objetos.

5) Existem duas formas de implementação de polimorfismo. Uma se dá quando se tem a


mesma operação implementada várias vezes na mesma classe e a outra acontece na
herança, quando a classe herdeira altera o método original. Assinale a alternativa
correta quanto ao nome desses tipos de implementação de polimorfismo:

A) Sobrecarga e Sobreposição.

B) Estático e Sobrecarga.

C) Dinâmico e Sobreposição.

242
D) Alternativo e Sobrecarga.

E) Alternativo e Sobreposição.

NA PRÁTICA

A herança, o polimorfismo e a reutilização de código estão relacionados em programação


orientada a objetos. Quando se fala de herança entre classes, fala-se implicitamente de
reutilização de código, pois herdar é minimizar a escrita de código. O polimorfismo entra como
recurso interessante que permite que apenas aqueles métodos que devem se comportar de forma
diferenciada em uma classe herdeira sejam reescritos sem a necessidade de reescrita total de
uma classe.

A herança é um conceito muito poderoso, uma vez possibilitar que os desenvolvedores


maximizem o uso dos métodos (comportamento) e dos dados (estrutura) dos objetos existentes.
Já o polimorfismo é a habilidade de objetos receberem a mesma mensagem e comportarem-se
de maneira diferente.

Neste Na Prática, você verá um exemplo da utilização de herança e polimorfismo em uma


aplicação escrita em Java para entender como esses conceitos se relacionam com o objetivo de
reduzir a necessidade de reescrita de código.

243
244
SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Herança na programação orientada a objetos

Neste vídeo, você entenderá, de forma bem didática, sobre o conceito de herança na
programação orientada a objetos.

Conteúdo interativo disponível na plataforma de ensino!

Conceituando polimorfismo

Para conhecer mais sobre o conceito de polimorfismo na programação orientada a objetos,


assista ao vídeo a seguir.

Conteúdo interativo disponível na plataforma de ensino!

Herança Múltipla com Python

Assista ao vídeo a seguir e conheça a aplicação do conceito de Herança Múltipla, utilizada na


linguagem de programação Python.

Conteúdo interativo disponível na plataforma de ensino!

245
Padrões de projeto orientados a objeto

APRESENTAÇÃO

Desenvolver software a partir do zero pode se tornar uma atividade um tanto complexa. Para
ajudar, existem padrões de projeto. Um projeto deve ser específico para atender o problema a
resolver, e genérico para ser usado em desenvolvimentos futuros.

Nesta Unidade de Aprendizagem estudaremos padrões de projeto, especificamente os padrões


GOF, por serem os padrões mais conhecidos.

Bons estudos.

Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados:

• Definir o que é um padrão de projeto.


• Identificar padrões de projeto GOF.
• Usar um padrão de projeto em uma aplicação.

DESAFIO

Você trabalha como analista/programador em uma fábrica de software e foi encarregado de criar
uma aplicação para fazer cálculos utilizando as quatro operações básicas da matemática. Esses
cálculos serão executados apenas entre dois números. Você deverá criar um projeto e um pacote;
logo a seguir, crie as classes necessárias orientadas pelo padrão solicitado, e não esqueça de
criar uma classe de controle para instanciar as outras classes e testar os resultados. Para a criação
da aplicação, utilize o padrão de projeto Strategy. Exporte o projeto em formato zip e envie.

INFOGRÁFICO

O infográfico traz mais informações sobre os conceitos de padrões de projeto.

246
CONTEÚDO DO LIVRO

Projetar software orientado a objetos é difícil, mas projetar software> reutilizável orientado a
objetos é ainda mais complicado. Você deve identificar objetos pertinentes, fatorá-los em
classes no nível correto de granularidade, definir as interfaces das classes, as hierarquias de
herança e estabelecer as relações-chave entre eles.

Acompanhe um trecho do livro Padrões de projeto, o qual serve de base teórica para esta
Unidade de Aprendizagem. Inicie o estudo pelo tópico Introdução e finalize em O que é um
padrão de projeto?

Boa leitura!

247
1
Introdução

Projetar software orientado a objetos é difícil, mas projetar software reutilizável


orientado a objetos é ainda mais complicado. Você deve identificar objetos pertinen-
tes, fatorá-los em classes no nível correto de granularidade, definir as interfaces das
classes, as hierarquias de herança e estabelecer as relações-chave entre eles. O seu
projeto deve ser específico para o problema a resolver, mas também genérico o
suficiente para atender problemas e requisitos futuros. Também deseja evitar o re-
projeto, ou pelo menos minimizá-lo. Os mais experientes projetistas de software
orientado a objetos lhe dirão que um projeto reutilizável e flexível é difícil, senão
impossível, de obter corretamente da primeira vez. Antes que um projeto esteja
terminado, eles normalmente tentam reutilizá-lo várias vezes, modificando-o a cada
vez.
Projetistas experientes realizam bons projetos, ao passo que novos projetistas
são sobrecarregados pelas opções disponíveis, tendendo a recair em técnicas não-
orientadas a objetos que já usavam antes. Leva um longo tempo para os novatos
aprenderem o que é realmente um bom projeto orientado a objetos. Os projetistas
experientes evidentemente sabem algo que os inexperientes não sabem. O que é?
Uma coisa que os melhores projetistas sabem que não devem fazer é resolver
cada problema a partir de princípios elementares ou do zero. Em vez disso, eles
reutilizam soluções que funcionaram no passado. Quando encontram uma boa
solução, eles a utilizam repetidamente. Conseqüentemente, você encontrará padrões,
de classes e de comunicação entre objetos, que reaparecem freqüentemente em
muitos sistemas orientados a objetos. Esses padrões resolvem problemas específicos
de projetos e tornam os projetos orientados a objetos mais flexíveis e, em última
instância, reutilizáveis. Eles ajudam os projetistas a reutilizar projetos bem-sucedidos
ao basear os novos projetos na experiência anterior. Um projetista que está familia-
rizado com tais padrões pode aplicá-los imediatamente a diferentes problemas de
projeto, sem necessidade de redescobri-los.

248
CAPÍTULO 1 INTRODUÇÃO

Uma analogia nos ajudará a ilustrar este ponto. Os novelistas ou autores de


roteiros (cinema, teatro, televisão) raramente projetam suas tramas do zero. Em
vez disso, eles seguem padrões como “O herói tragicamente problemático”
(Macbeth, Hamlet, etc.) ou “A Novela Romântica” (um sem-número de novelas
de romances). Do mesmo modo, projetistas de software orientado a objetos
seguem padrões como “represente estados como objetos” e “adorne objetos de
maneira que possa facilmente acrescentar/remover características”. Uma vez
que você conhece o padrão, uma grande quantidade de decisões de projeto
decorre automaticamente.
Todos sabemos o valor da experiência de projeto. Quantas vezes você já não
passou pela experiência do déja vu durante um projeto – aquele sentimento de que já
resolveu um problema parecido antes, embora não sabendo exatamente onde e
como? Se pudesse lembrar os detalhes do problema anterior e de que forma o
resolveu, então poderia reutilizar a experiência em lugar de redescobri-la. Contudo,
nós não fazemos um bom trabalho ao registrar experiência em projeto de software
para uso de outros.
A finalidade deste livro é registrar a experiência no projeto de software
orientado a objetos sob a forma de padrões de projeto. Cada padrão de projeto
sistematicamente nomeia, explica e avalia um aspecto de projeto importante e
recorrente em sistemas orientados a objetos. O nosso objetivo é capturar a experiência
de projeto de uma forma que as pessoas possam usá-la efetivamente. Com esta
finalidade em vista, documentamos alguns dos mais importantes padrões de projeto
e os apresentamos em um catálogo.
Os padrões de projeto tornam mais fácil reutilizar projetos e arquiteturas bem-
sucedidas. Expressar técnicas testadas e aprovadas as torna mais acessíveis para os
desenvolvedores de novos sistemas. Os padrões de projeto ajudam a escolher
alternativas de projeto que tornam um sistema reutilizável e a evitar alternativas que
comprometam a reutilização. Os padrões de projeto podem melhorar a documenta-
ção e a manutenção de sistemas ao fornecer uma especificação explícita de interações
de classes e objetos e o seu objetivo subjacente. Em suma, ajudam um projetista a obter
mais rapidamente um projeto adequado.
Nenhum dos padrões de projeto descreve projetos novos ou não-testados.
Incluimos somente projetos que foram aplicados mais de uma vez em diferentes
sistemas. Muitos deles nunca foram documentados antes. São parte do folclore da
comunidade de desempenho de software orientado a objetos ou elementos de
sistemas orientados a objetos bem-sucedidos — em nenhum dos casos é fácil para
projetistas novatos aprender as lições. Assim, embora esses projetos não sejam novos,
nós os capturamos numa forma nova e acessível: como um catálogo de padrões, que
tem um formato consistente.
Apesar do tamanho do livro, os padrões apresentados capturam somente uma
fração do que um especialista pode conhecer. Não há nenhum padrão que lide com
concorrência ou programação distribuída ou programação para tempo real. O livro
não tem nenhum padrão específico para um domínio de aplicação. Não diz como
construir interfaces para usuário, como escrever device drivers, ou como usar um
banco de dados orientado a objetos. Cada uma dessas áreas tem seus próprios
padrões, e valeria a pena alguém catalogá-los também.*

* N. de R. T: Atualmente, já existe literatura disponível sobre padrões para domínios específicos. Ver Analysis
Patterns e Enterprise Integration Patterns ambos de Martin Fowler.

249
PADRÕES DE PROJETO

1.1 O que é um padrão de projeto?


Christopher Alexander afirma: “cada padrão descreve um problema no nosso
ambiente e o cerne da sua solução, de tal forma que você possa usar essa solução mais
de um milhão de vezes, sem nunca fazê-lo da mesma maneira ” [AIS+77, pág. x].
Muito embora Alexander estivesse falando acerca de padrões em construções e
cidades, o que ele diz é verdadeiro em relação aos padrões de projeto orientados a
objeto. Nossas soluções são expressas em termos de objetos e interfaces em vez de
paredes e portas, mas no cerne de ambos os tipos de padrões está a solução para um
problema num determinado contexto.
Em geral, um padrão tem quatro elementos essenciais:

1. O nome do padrão é uma referência que podemos usar para descrever um


problema de projeto, suas soluções e conseqüências em uma ou duas pala-
vras. Dar nome a um padrão aumenta imediatamente o nosso vocabulário de
projeto. Isso nos permite projetar em um nível mais alto de abstração. Ter um
vocabulário para padrões permite-nos conversar sobre eles com nossos
colegas, em nossa documentação e até com nós mesmos. O nome torna mais
fácil pensar sobre projetos e a comunicá-los, bem como os custos e benefícios
envolvidos, a outras pessoas. Encontrar bons nomes foi uma das partes mais
difíceis do desenvolvimento do nosso catálogo.
2. O problema descreve em que situação aplicar o padrão. Ele explica o
problema e seu contexto. Pode descrever problemas de projeto específicos,
tais como representar algoritmos como objetos. Pode descrever estruturas de
classe ou objeto sintomáticas de um projeto inflexível. Algumas vezes, o
problema incluirá uma lista de condições que devem ser satisfeitas para que
faça sentido aplicar o padrão.
3. A solução descreve os elementos que compõem o padrão de projeto, seus
relacionamentos, suas responsabilidades e colaborações. A solução não
descreve um projeto concreto ou uma implementação em particular porque
um padrão é como um gabarito que pode ser aplicado em muitas situações
diferentes. Em vez disso, o padrão fornece uma descrição abstrata de um
problema de projeto e de como um arranjo geral de elementos (classes e
objetos, no nosso caso) o resolve.
4. As conseqüências são os resultados e análises das vantagens e desvantagens
(trade-offs) da aplicação do padrão. Embora as conseqüências sejam raramen-
te mencionadas quando descrevemos decisões de projeto, elas são críticas
para a avaliação de alternativas de projetos e para a compreensão dos custos
e benefícios da aplicação do padrão.
As conseqüências para o software freqüentemente envolvem balanceamento
entre espaço e tempo. Elas também podem abordar aspectos sobre lingua-
gens e implementação. Uma vez que a reutilização é freqüentemente um
fator no projeto orientado a objetos, as conseqüências de um padrão incluem
o seu impacto sobre a flexibilidade, a extensibilidade ou a portabilidade de
um sistema. Relacionar essas conseqüências explicitamente ajuda a
compreendê-las e avaliá-las.

O ponto de vista afeta a interpretação de alguém sobre o que é, ou não, um


padrão. O padrão de uma pessoa pode ser um bloco de construção primário para

250
CAPÍTULO 1 INTRODUÇÃO

outra. Neste livro concentramos-nos sobre os padrões que estão em um certo nível de
abstração. Padrões de projeto não são projetos, como listas encadeadas e tabelas de
acesso aleatório, que podem ser codificadas em classes e ser reutilizadas tais como
estão. Tampouco são projetos complexos, de domínio específico, para uma aplicação
inteira ou subsistema. Padrões de projeto, neste livro, são descrições de objetos e classes
comunicantes que precisam ser personalizadas para resolver um problema geral de projeto
num contexto particular.
Um padrão de projeto nomeia, abstrai e identifica os aspectos-chave de uma
estrutura de projeto comum para torná-la útil para a criação de um projeto orientado
a objetos reutilizável. O padrão de projeto identifica as classes e instâncias participan-
tes, seus papéis, colaborações e a distribuição de responsabilidades. Cada padrão de
projeto focaliza um problema ou tópico particular de projeto orientado a objetos. Ele
descreve em que situação pode ser aplicado, se ele pode ser aplicado em função de
outras restrições de projeto e as conseqüências, custos e benefícios de sua utilização.
Uma vez que em algum momento devemos implementar nossos projetos, um padrão
de projeto também fornece exemplos em código – nesse caso, C++ e, algumas vezes,
Smalltalk – para ilustrar uma implementação.
Embora padrões de projeto descrevam projetos orientados a objeto, baseiam-se
em soluções reais que foram implementadas nas principais linguagens de programa-
ção orientadas a objeto, como Smalltalk e C++, em vez de implementações em
linguagens procedurais (Pascal, C, Ada) ou linguagens orientadas a objetos mais
dinâmicas (CLOS, Dylan, Self). Nós escolhemos Smalltalk e C++ por razões práticas:
a nossa experiência do dia a dia foi com estas linguagens e elas estão se tornando cada
vez mais populares.*
A escolha da linguagem de programação é importante porque influencia o
ponto de vista do projetista (usuário do pradrão): nossos padrões assumem recursos
de linguagem do nível do Smalltalk/C++, e essa escolha determina o que pode, ou
não, ser implementado facilmente. Se tivéssemos assumido o uso de linguagens
procedurais, deveríamos ter incluído padrões de projetos como “Herança”, “Encap-
sulamento” e “Polimorfismo”. De maneira semelhante, alguns dos nossos padrões
são suportados diretamente por linguagens orientadas a objetos menos comuns. Por
exemplo, CLOS tem multimétodos que diminuem a necessidade de um padrão como
Visitor (pág. 305). De fato, há bastante diferenças entre Smalltalk e C++, o que
significa que alguns padrões podem ser expressos mais facilmente em uma lingua-
gem que em outra. (Ver Iterator, 244, por exemplo).

* N. de R. T: Essa observação foi feita em 1995, quando, de fato, as linguagens C++ e Smalltalk ganharam grande
reconhecimento.
251
DICA DO PROFESSOR

Assista ao vídeo e conheça mais sobre os conceitos de padrões de projetos.

Conteúdo interativo disponível na plataforma de ensino!

EXERCÍCIOS

1) Marque a alternativa incorreta referente a padrões de projeto.

A) Projetar software reutilizável orientado a objetos é uma tarefa complexa.

B) Projetistas experientes consideram que projetar software orientado a objetos reutilizável e


flexível é difícil e até impossível de obter corretamente da primeira vez.

C) Bons projetistas sabem que não devem fazer e resolver problemas a partir dos princípios
elementares ou do zero.

D) Projetistas, quando encontram uma solução, reutilizam-na várias vezes.

E) Padrões de projeto não conseguem resolver problemas específicos.

2) Em geral, um padrão tem quatro elementos essenciais. Marque a alternativa que


descreve de forma incorreta o elemento.

A) O nome do padrão é a referência que podemos utilizar para descrever um problema de


projeto.

B) O problema descreve em que situação aplicar o padrão.

252
C) A solução do problema incluirá uma lista de condições que devem ser satisfeitas para que
faça sentido aplicar padrão.

D) As consequências são o resultado da análise das vantagens e desvantagens (trade-offs) da


aplicação do padrão.

E) A solução descreve os elementos que compõem o padrão de projetos, seus


relacionamentos, suas responsabilidades e colaborações.

3) O catálogo de padrões de projetos escrito pelo GOF tem 23 padrões. Os padrões de


projeto variam na sua granularidade e no seu nível de abstração. Como existem
muitos padrões, eles foram organizados. Com isso, os padrões de projeto foram
classificados por dois critérios: o primeiro chama-se finalidade e o segundo, escopo.
Referente ao primeiro critério, os padrões podem ter a finalidade de criação,
estrutural ou comportamental. Marque a finalidade que lida com a composição de
classes e objetos.

A) Criação.

B) Escopo.

C) Estrutural.

D) Comportamental.

E) Nenhuma das finalidades citadas.

4) Marque a alternativa que melhor descreve o padrão Strategy.

A) Fornece uma interface para a criação de famílias de objetos relacionados ou dependentes


sem especificar suas classes concretas.

253
B) Representa uma operação a ser executada sobre os elementos da estrutura de um objeto.

C) Permite que um objeto altere seu comportamento quando seu estado interno muda.

D) Define uma família de algoritmos, encapsula cada um deles e torna-os intercambiáveis.

E) Sem violar o encapsulamento, captura e externaliza um estado interno de um objeto, de


modo que o mesmo possa, posteriormente, ser restaurado para este estado.

5) Marque a alternativa incorreta.

A) a) O critério "escopo" especifica se o padrão se aplica primeiramente a classes ou a


objetos.

B) b) O padrão para classes lida com os relacionamentos entre classes e suas subclasses.

C) c) Alguns padrões são frequentemente usados em conjuntos.

D) d) Os padrões Factor Method, Abstract Factor, Builder, Prototype e Singleton possuem a


finalidade de Criação.

E) e) Os padrões com propósito estrutural são: Adapter(class), Adapter(Object), Bridge,


Composite, Decorator, Facade, Flyweight, Proxy, Visotor.

NA PRÁTICA

Imagine uma empresa da construção civil que construirá vários imóveis. Nessa atividade,
percebe-se que todos os banheiros são semelhantes, com isso, a experiência empregada na
construção de um deles pode ser aplicada a todos. Portanto, estamos criando um padrão de
projeto. Quando construímos um software, criamos formas para resolver um determinado

254
problema, e, com nossa experiência, repetimos o processo em outras construções, criando um
padrão de projeto.

Assita no vídeo outro exemplo:

Conteúdo interativo disponível na plataforma de ensino!

SAIBA MAIS

Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do
professor:

Padrão de projeto Strategy.

Conteúdo interativo disponível na plataforma de ensino!

Entendendo os conceitos dos padrões de projeto em Java.

Conteúdo interativo disponível na plataforma de ensino!

255

Você também pode gostar