Você está na página 1de 1265

Programação Orientada por Objetos

Introdução à
gestão de
versões
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ A Gestão de Versão

▸ Sistemas de Gestão de Versão

▸ Git, GitHub, GitHub Classroom

▸ Utilização de Git

2
Problema

▸ “Piled Higher and Deeper” por Jorge


Cham, http://www.phdcomics.com

3
Problema
▸ Imagine que escreveu um programa que estava
a funcionar mas que depois o estragou ao
implementar uma nova funcionalidade.
▹ Como iria conseguir recuperar a versão
funcional?
▹ É mesmo possível?
▹ Como fez no projeto da disciplina de IPOO?

4
Solução
▸ Recuperar a versão funcional do seu código só
é possível se tiver criado uma cópia da versão
antiga do código.
▸ O perigo de perder as versões funcionais
geralmente leva ao fluxo de trabalho
problemático ilustrado no cartoon PhD Comics
mostrado anteriormente.

5
“ ▸ O controlo de versão é
usado para rastrear e
armazenar alterações
num conjunto de
ficheiros sem perder o
histórico de alterações
anteriores.
6
Sistema de controlo de versão

▸ Um sistema de controlo de versão é uma ferramenta que rastreia essas mudanças


para nós, criando efetivamente diferentes versões dos nossos ficheiros.
▸ Os sistemas de controlo de versões começam com uma versão base do
documento e, a seguir, são guardadas apenas as alterações feitas em cada
etapa do processo.

7
O que é controlo de versão?

▸ Permite-nos decidir quais mudanças serão feitas na próxima versão


(cada registo dessas mudanças é chamado de commit), e mantém
metadados úteis sobre eles.
▸ O histórico completo de commits para um projeto específico e os seus
metadados constituem um repositório.
▸ Os repositórios podem ser mantidos e sincronizados em diferentes
computadores, facilitando a colaboração entre diferentes pessoas.

8
Aspectos chave
▸ O controlo de versão permite:
▹ Voltar facilmente à uma versão anterior
▹ O controlo de versão é como um "undo" ilimitado.
▹ Seguir a evolução do projeto ao longo do tempo
▹ O trabalho em paralelo em partes disjuntas do projeto e gerir as modificações
concorrentes
▹ Facilitar a deteção de erros
▹ ….

9
Histórico
▸ Anos 1980: sistemas automatizados de controle
▹ RCS, CVS ou Subversion
▹ usados por muitas empresas grandes.
▹ Sistemas desatualizados devido a várias limitações nas suas
funcionalidade.
▸ Sistemas mais modernos: como Git e Mercurial,
▹ Distribuídos: não precisam de um servidor centralizado para
alojar o repositório.
▹ Ferramentas de fusão poderosas (merging) : vários autores
trabalhem nos mesmos ficheiros simultaneamente.
10
Sistema local
Vantagens
Gestão e utilização muito
simples

Desvantagens
Muito sensível às avarias
Não permite a colaboração

11
Sistema centralizado
Vantagens
▸ Estrutura simples
▸ Gestão e utilização muito
simples

Desvantagens
▸ Muito sensível às avarias
▸ Não adaptado aos grandes
projetos e / ou à uma estrutura
muito hierarquizada a
colaboração
12
Sistema distribuído
Vantagens
▸ Menos sensível às avarias
▸ Adaptado aos grandes projetos e / ou
à uma estrutura muito hierarquizada

Desvantagens
▸ Gestão e utilização mais
complicadas
▸ Pode ter uma estrutura muito
complexa

13
git - sistema de gestão de versão distribuído

▸ Histórico (Wikipedia)
▹ De 1991 a 2002, o núcleo de Linux estava a ser desenvolvido
sem usar sistema de gestão de versão.
▹ A partir de 2002, a comunidade começou a usar BitKeeper,
um DVCS proprietário.
▹ Em 2005, depois de um contencioso, BitKeeper retira a
possibilidade de usar gratuitamente o seu produto. Linus
Torvalds lança o desenvolvimento de Git e após alguns
meses de desenvolvimento, Git aloja o desenvolvimento do
núcleo Linux
14
Git – princípios de base
▸ Um repositório Git é uma espécie de sistema de ficheiros (base de dados),
que guarda as versões dos ficheiros de um projeto em determinados
momentos, ao longo do tempo sob a forma de instantâneos (snapshots).

15
Git – princípios de base
3 zonas num projeto Git
▹ O repositório Git: contém os
metadados e a base de dados dos
objetos do projeto
▹ O diretório de trabalho: extração
única da versão do projeto desde a
base de dados do repositório
▹ A zona de trânsito / índex: simples
ficheiro que contém as informações
sobre o que será considerado na
próxima submissão/commit.
16
Git – princípios de base
4 estados de um ficheiro no Git
▹ não “versionado”: ficheiro não gerido ou
que deixou de ser gerido pelo Git;
▹ não modificado: ficheiro guardado de
forma segura na sua versão atual na base
de dados do repositório;
▹ modificado: ficheiro que sofreu
modificações desde a última vez que houve
um commit;
▹ Indexado (staged): idêntico ao modificado,
só que será integrado no próximo
instantâneo na sua versão corrente durante
o próximo commit
17
Git – princípios de base
Cada submissão (commit) dá lugar à criação de um objeto “commit” que contém um apontador para um
instantâneo do conteúdo cujas modificações estavam indexadas no momento da submissão (conjunto de
objetos que permitem armazenar um instantâneo dos ficheiros em causa e por outro lado eu permitem
reproduzir a estrutura do repositório do projeto e dos subdiretórios), alguns metadados (autor, mensagem) e
um apontador para o objeto ”commit” anterior.

18
Git – princípios de base

19
Git – princípios de base

20
Como os comandos Git funcionam
Área de
Diretório de Repositório Repositório
encenação
trabalho Local Remoto
(stage)

git add git commit git push

git merge git fetch

git pull

git checkout git clone


21
Remoto

Como o Git funciona


git push repositório remoto

git fetch
git fetch git push

Local Local
repositório local repositório local

git commit git commit

git checkout área de stage área de stage

git checkout
git add git add

diretório local diretório local

edita edita

programador programador
22
Git – comandos de base
Inicializar um repositório $ git init

Mostrar o estado dos ficheiros do repertório $ git status


corrente
▸ Untracked files: ficheiros não versionados
▸ Changes to be committed: modificações
(adição,remoção,mudança) carregadas na zona
de trânsito (staging area), ou indexadas,
▸ Changes not staged for commit: moficiações
que não foram acrregadas para a zona de trânsito
(ou indexadas).

23
Git – comandos de base
Indexar a adição ou modificações de um ficheiro $ git add [-p] <ficheiro>

Cancelar as modificações indexadas de um ficheiro $ git reset <ficheiro>

Cancelar as modificações ainda não indexadas de um $ git checkout [--] <ficheiro>


ficheiro

$ git rm <ficheiro>
Indexar a eliminação de um ficheiro

$ git rm --cached <ficheiro>


“Deversionar” um ficheiro

24
Git – comandos de base
Mostrar o detalhe de modificações não indexadas $ git diff

Mostrar o detalhe das modificações indexadas $ git diff --staged

Submeter as modificações indexadas em zona de trânsito $ git commit

Ver o histórico das submissões $ git log

25
Ramos
▸ Um ramo no Git é simplesmente um ponteiro para um objeto ”
commit”. Por defeito, existe um único ramo, chamado “master”.
▸ HEAD é um ponteiro especial para o ramo para o qual estamos a
trabalhar atualmente (extraída do repositório)

26
Trabalho em equipa

Ana

João

27
Comandos para os ramos
Criar um ramo $ git branch <ramo>

Ver os ramos do repositório local $ git branch

Eliminar um ramo $ git branch –d <ramo>

Saltar par um determinado ramo $ git checkout <ramo>

28
Git - ramos

Duas formas de incluir as modificações de uma ramo num outro ramo corrente.

Redifinir a
Fusionar base (rebase)
(merge)

29
Git - ramos
▸ Fusionar as modificações de uma dado ramo no ramo corrente (HEAD)
$ git merge <ramo>
▹ Se o objeto “commit” apontado por <ramo> jà é um antepassado do objeto
”commit” corrente (HEAD), então nada é feito.
▹ Se o objeto “commit” apontado por <ramo> jà é um descendente do objeto
”commit” corrente, só o apontador do ramo corrente é movido para o
objeto “commit” abrangido pela fusão (“fast-forward”).

30
Git - ramos

31
Git - ramos
Em caso de conflito
▸ Nenhum objeto “commit” de fusão é criado, mas o processo entra em pausa.
▸ git status dá os ficheiros que não puderam ser fusionados (listados
como “unmerged”).
▸ Git adiciona marcadores de resolução de conflitos à cada ficheiro sujeito a
conflitos afim que possam ser resolvidos manualmente.
▸ Para marcar os conflitos num ficheiro <ficheiro> como resolvido, tem-se
de se fazer git add <ficheiro>. Podemos, após resolução de todos os
conflitos, submeter as modificações sob a forma de objeto “commit” de
fusão com git commit e terminar assim o processo de fusão.
32
Git - ramos
Rebasear as modificações do ramo corrente (HEAD) num dado ramo
$ git rebase <ramo>
▸ Ir ao objeto ”commit” correspondente ao antepassado mais novo comum aos dois objetos
“commit” apontados pelo ramo corrente e <ramo> .
▸ Obter e salvaguardar as mudanças introduzidas desde este ponto para cada objeto “commit”
do ramo corrente.
▸ Fazer apontar o ramo corrente para o mesmo objeto “commit” que <ramo> e reaplicar todas
as modificações uma a uma.

33
Git – trabalho com repositórios remotos
Para colaborar, é necessário comunicar com um ou mais repositórios remotos alojando o
mesmo projeto (tipicamente repositórios públicos associados à uma pessoa, uma equipa ou
todo o projeto).

Os dados dos repositórios remotos (objetos “commits” e instantâneos) são inteiramente


copiados no repositório local, e para cada ramo <ramo> de um repositório remoto
<repositório> é mantida um ramo local <repositório>/<ramo> não modificável, permitindo de
seguir a posição de <ramo> no <repositório> localmente.

▸ Mostrar a lista de todos os repositórios remotos do projeto


$ git remote
34
Git – trabalho com repositórios remotos
▸ Clonar um repositório remoto $ git clone <URL> [<repositório>]

35
Git – trabalho com repositórios remotos
▸ Ir buscar as modificações de um repositório remoto

$ git fetch <repositório>

36
Git – trabalho com repositórios remotos
▸ Adicionar um repositório remoto $ git remote add <nome> <URL>

37
Git – trabalho com repositórios remotos
▸ Atualizar um dado repositório remoto com um dado ramo local
$ git push <repositorio> <ramo>
Cuidado: só funciona se foi-se buscar o último objeto commit do ramo abrangido do
repositório remoto e tiver sido integrado no ramo do repositório local, i.e., se a
atualização do ramo do repositório remoto pode ser feita pelo “fast-foward”.
▸ Combinar git fetch e git merge
$ git pull <repositorio> <ramo>
▸ Combinar git fetch e git rebase

$ git pull --rebase <repositorio> <ramo>

38
Git – Conselhos e boas práticas

Conselhos Boas Práticas


▸ Usar e abusar dos ramos ▸ Nunca rebasear um ramo num repositório
▸ Evitar de fazer público
sistematicamente um git ▸ Respeitar as convenções de formatação
pull, mas pensar no que é da mensagem de submissão
pertinente fazer (fusionar ▹ Título de 50 caracteres no máximo, seguido de
ou alterar a base) uma linha vazia e de uma descrição detalhada
com linhas de 72 caracteres no máximo, usando
o presente: https://tbaggery.com/2008/04/19/a-
note
39
Demo

40
Resumo
▸ O que é controlo de versões e porque devemos usá-lo.
▸ Compreender os benefícios de um sistema de controle de versão
automatizado.
▸ Compreender os princípios básicos de como funcionam os
sistemas automatizados de controle de versão.

41
Bibliografia
▸ Pro Git, Scott Chacon & Ben Straub Apress,
2014
▹ Git - Book (git-scm.com)

42
Glossário
▸ changeset / conjunto de mudanças
▹ Um grupo de mudanças num ou mais ficheiros que são ou serão adicionados à um único commit
num repositório de controle de versão.
▸ commit / confirmação
▹ Como verbo, significa registar o estado atual de um conjunto de ficheiros (um changeset) num
repositório de controle de versão. Como substantivo, designa o resultado da confirmação, ou seja,
um conjunto de alterações registado num repositório. Se um commit contém mudanças em vários
ficheiros, todas as mudanças são registadas juntas.
▸ conflict / conflito
▹ Uma alteração feita por um utilizador de um sistema de controle de versão que é incompatível com
as alterações feitas por outros utilizadores. Ajudar os utilizadores a resolver conflitos é uma das
principais tarefas do controle de versão.

43
Glossário
▸ HTTP
▹ O protocolo de transferência de hipertexto usado para partilhar páginas da web e outros
dados na World Wide Web.
▸ merge / fusão
▹ (aplicado à um repositório): Para reconciliar dois conjuntos de alterações num repositório.
▸ protocol / protocolo
▹ Um conjunto de regras que definem como um computador comunica com outro. Os
protocolos comuns na Internet incluem HTTP e SSH.
▸ remote / remoto
▹ (aplicado à um repositório) Um repositório de controle de versão conectado a outro, de
forma que ambos possam ser mantidos sincronizados trocando commits.

44
Glossário
▸ repository / repositório
▹ Uma área de armazenamento onde um sistema de controle de versão armazena o
histórico completo de commits de um projeto e informações sobre quem mudou o quê e
quando.
▸ resolve / resolver
▹ Para eliminar os conflitos entre duas ou mais alterações incompatíveis num ficheiro ou
conjunto de ficheiros geridos por um sistema de controle de versão.
▸ revision / revisão
▹ Um sinônimo para commit.

45
Glossário
▸ SHA-1
▹ SHA-1 Hashes é o método que o Git usa para processar identificadores, inclusive para commits. Para
processá-los, Git usa não apenas a mudança real de um commit, mas também os seus metadados (como
data, autor, mensagem), incluindo os identificadores de todos os commits de mudanças anteriores. Isso
torna os IDs de commit do Git virtualmente únicos. Ou seja, a probabilidade de que dois commits feitos
independentemente, mesmo com a mesma mudança, recebam o mesmo ID é extremamente pequena.
▸ SSH
▹ O protocolo Secure Shell usado para uma comunicação segura entre computadores.
▸ timestamp
▹ Um registo de quando um determinado evento ocorreu.
▸ version control / controle de versão
▹ Uma ferramenta para gerir mudanças num conjunto de ficheiros. Cada conjunto de mudanças cria um
novo commit dos ficheiros; o sistema de controle de versão permite que os utilizadores recuperem
commits antigos de forma
46
Programação Orientada por Objetos

Desenho de
aplicações
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Método dos verbos/substantivos

▸ Cartas CRC e Cenários

▸ Outros Aspetos do Desenvolvimento

▸ Padrões de Desenho

2
Método verbos/substantivos

▸ Desenho de aplicações

3
Abordagem ao desenvolvimento de projetos em
POO
Documento de partida

Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis
4
Documento de partida

Documento de partida
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Descrição informal do sistema / aplicação, numa perspetiva de alto nível

▹ Descrição dos objetivos gerais do sistema/aplicação

▹ Descrição mais detalhada das funcionalidades

▹ Descrição de cenários de utilização/interação

5
Documento de partida

Modelação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Análise e modelação do sistema a construir


▸ A aplicação deve realizar um conjunto de funcionalidades. Tais funcionalidades têm
que ser identificadas e nomeadas.
▹ Quais são?
▸ Cada funcionalidade tem que ser caracterizada
▹ O que faz?
▸ O conjunto das funcionalidades permite definir um conjunto de serviços de topo
▸ Estas funcionalidades poderão mais tarde ser refinadas

6
Documento de partida

Modelação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Análise e modelação do sistema a construir (cont.)


▹ Deve-se assegurar (explicar / justificar) que as operações dessas funcionalidades
de topo permitem realizar os cenários de utilização / interação
▹ Análise da realização de cada operação / funcionalidade de topo com a
introdução de:
▹ entidades (que encapsulam dados e expõem operações)
▹ as operações das entidades
▹ Por enquanto, preocupamo-nos sobretudo com o encapsulamento e a ocultação
de informação
7
Documento de partida

Modelação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Output da fase de modelação:


▹ Identificação das funcionalidades (serviços) da aplicação
▹ explicação informal da funcionalidade de cada operação
▸ Identificação das entidades do modelo
▹ Nome
▹ Lista de operações
▹ Explicação informal do efeito de cada operação de cada entidade

8
Documento de partida

Modelação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Output da fase de modelação (cont.):


▹ Explicação de como os serviços, operações e entidades identificadas
contribuem para realizar as funcionalidades caracterizadas anteriormente.
▹ Visão organizada da estrutura do sistema / aplicação a desenvolver em
termos de
▹ uma unidade principal (lista de funcionalidades / serviços prestados pela
aplicação)
▹ um conjunto de entidades e suas operações
▹ Independente da representação de dados
▹ Independente de algoritmos utilizados, na medida do possível.
9
Documento de partida

Construção e Validação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Começar pelo “esqueleto da aplicação”


▸ Os serviços, as entidades, operações concebidas, na fase anterior devem ser implementadas
▹ Identificar as classes que as suportam
▹ Escolher, para cada classe, uma representação adequada
▹ Operações de funcionalidades são publicas
▹ Operações auxiliares devem ficar escondidas (privadas)
▹ Variáveis e constantes para representar o estado
▹ Para cada uma indicar o seu tipo e finalidade
▹ Podemos usar objetos de um tipo como valores de variáveis usadas dentro de outros
objetos
10
Documento de partida

Construção e Validação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Nesta altura, deverá ter um esqueleto da sua aplicação


▹ Funcionalidades (conjunto de métodos públicos)
▹ Classes
▹ Métodos (por enquanto, vazios)
▹ Constantes e variáveis
▹ Documentação
▸ Programa ainda não passa no compilador

11
Documento de partida

Construção e Validação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Construção do sistema
▹ Não tentar fazer tudo ao mesmo tempo!
▹ Definir sequência de implementação que permita ir validando incrementalmente o seu programa
▹ Exemplo:
▹ Interpretador de comandos
▹ Programa aceita comandos e dados associados, mas (ainda) não faz nada com eles
▹ Implementação de comandos
▹ Escolher uma sequência para a implementação de comandos que permita ir
testando os comandos, à medida que os implementa

12
Documento de partida

Construção e Validação
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ Indicações gerais
▹ Tente ter sempre uma versão estável do seu programa o Compila sem erros
▹ Implementa algumas das funcionalidades do seu programa
▹ Está bem testada
▹ Teste as suas classes, método a método
▹ Se necessário, construa pequenos programas de teste
▹ Não avance para novas fases do desenvolvimento, sem as anteriores estarem sólidas
▹ Use sempre o melhor estilo de programação e documentação possível

13
Documento de partida

Entregáveis
Fase 1 Modelação

Fase 2 Construção e Validação

Entregáveis

▸ No final do desenvolvimento, deve disponibilizar


▹ Código fonte do seu programa, devidamente comentado, (pode incluir classes de testes)
▹ Documentação completa
▹ Javadoc
▹ Diagramas de classes e interfaces
▹ Outros que lhe sejam solicitados
▹ Manual de instalação e utilização
▹ ...

14
Exemplo – Sistema de reservas
▸ Sistema de reserva de lugares para cinemas.
▹ O sistema de reservas do cinema deve guardar as reservas de lugares para várias salas.
▹ Cada sala tem os lugares dispostos por filas.

▹ Os clientes podem reservar os lugares e ficam com a letra da fila e o número do lugar.
▹ Eles podem solicitar a reserva de vários lugares adjacentes.
▹ Cada reserva é para uma determinada sessão (ou seja a projeção de um dado filme a uma
certa hora).
▹ As sessões têm uma data e hora e estão escalonadas para uma determinada sala.

▹ O sistema guarda o número do telefone do cliente.

15
Exemplo – Sistema de reservas
▸ Diagrama de classes da aplicação Sistema de reservas:

16
Exemplo – Sistema de reservas
▸ Quais são as classes da aplicação?
▹ Até agora de uma forma ou de outra tínhamos as classes da solução. Num projeto de software real
temos de encontrar essas classes, o que não é uma tarefa simples.
▹ Os passos iniciais do desenvolvimento de software são a análise e o design.
▹ Analisa-se o problema.
▹ Desenha-se a solução (o design da solução).
▹ Não existe um método bem definido e único para encontrarmos as classes da solução. Algumas
abordagens simples são:
▹ O método verbos/substantivos.
▹ A utilização de cartas CRC. 17
Método verbo/substantivo
▸ Quais são as classes da aplicação?
▹ Os substantivos descrevem “coisas”.

▹ São uma fonte de referência de classes e objetos.

▹ Os verbos referem ações.

▹ Podem ser a fonte para encontrarmos as interações entre objetos.

▹ Como as ações representam comportamentos, logo referem métodos.

18
Exemplo – Sistema de reservas
▸ Verbos e substantivos
▹ O sistema de reservas do cinema deve guardar as reservas de lugares para várias salas.
▹ Cada sala tem os lugares dispostos por filas.
▹ Os clientes podem reservar os lugares e ficam com a letra da fila e o número do lugar.
▹ Eles podem solicitar a reserva de vários lugares adjacentes.
▹ Cada reserva é para uma determinada sessão (ou seja a projeção de um dado filme a uma certa hora).
▹ As sessões têm uma data e hora e estão escalonadas para uma determinada sala.
▹ O sistema guarda o número do telefone do cliente.

19
Exemplo – Sistema de reservas
▸ Verbos e substantivos

▹ Vamos procurar os nomes (os substantivos) que nos


irão dar uma pista dos objetos e classes envolvidos.

▹ Depois procuramos os verbos juntamente com os


substantivos a que se referem.
20
Exemplo – Sistema de reservas
▸ Substantivos

▹ O sistema de reservas do cinema deve guardar as reservas de lugares para várias salas.

▹ Cada sala tem os lugares dispostos por filas.

▹ Os clientes podem reservar os lugares e ficam com a letra da fila e o número do lugar.

▹ Eles podem solicitar a reserva de vários lugares adjacentes.

▹ Cada reserva é para uma determinada sessão (ou seja a projeção de um dado filme a uma certa hora).

▹ As sessões têm uma data e hora e estão escalonadas para uma determinada sala.

▹ O sistema guarda o número do telefone do cliente.

21
Exemplo – Sistema de reservas
▸ Verbos e substantivos
▹ O sistema de reservas do cinema deve guardar as reservas de lugares para várias salas.

▹ Cada sala tem os lugares dispostos por filas.

▹ Os clientes podem reservar os lugares e ficam com a letra da fila e o número do lugar.

▹ Eles podem solicitar a reserva de vários lugares adjacentes.

▹ Cada reserva é para uma determinada sessão (ou seja a projeção de um dado filme a uma certa hora).

▹ As sessões têm uma data e hora e estão escalonadas para uma determinada sala.

▹ O sistema guarda o número do telefone do cliente. 22


Exemplo – Sistema de reservas
▸ Verbos e substantivos Sistema de reservas Sala Filme
Guarda (reserva de lugares) Tem (lugares)
Guarda (número telefone)

Cliente Hora Data


Reserva (lugares)
Ficam com (letra fila, número lugar)
Solicitam (reserva de lugar) Reserva de lugar

Sessão Lugar
Escalonada para (sala)

Número telefone Fila

23
Cartas CRC e
Cenários
Desenho de aplicações

24
Cartas CRC
▸ Cartas CRC

▹ CRC significa:
▹ C – Classes
▹ R – Responsabilidades
▹ C - Colaboradores

▹ Utilizam-se cartas reais divididas em áreas para cada um dos


componentes identificados.
▹ Este método foi inicialmente descrito por Kent Beck e Ward Cunningham.
25
Cartas CRC
Classe (nome) Colaboradores

▸ A carta CRC Responsabilidades

26
Cenários
▸ Cenários
▹ Depois de se obterem as possíveis classes do sistema e que são identificadas nas
cartas CRC é necessário descobrir as interações entre elas e isso será feito através
de Cenários.
▹ Um cenário descreve um caso concreto da realização de uma atividade no sistema.

▹ Vamos identificar o que se faz (em cada classe) quando se utiliza o sistema.
▹ É uma forma de encontrar as interações entre objetos (colaborações).

▹ Pode ser feito como uma atividade de grupo.

27
Cenários – exemplo prático
SistemaReservas Colaboradores
Pode encontrar as sessões Sessão
pelo título do filme e pelo
dia

Guarda coleções de Coleção


sessões

Obtém e mostra os
detalhes das sessões

28
Cenários
▸ Cenários
▹ Os cenários permitem verificar se a descrição do problema é
clara e se está completa.
▹ É necessário perder algum tempo a encontrar os vários cenários
de utilização.
▹ A análise efetuada do problema leva-nos à solução
▹ Se encontrarmos os erros e omissões nesta fase estaremos
a poupar tempo e esforço desperdiçados mais tarde.
29
Desenho de classes
▸ Desenho de classes
▹ A análise dos cenários ajuda a clarificar a estrutura da aplicação
▹ Cada carta está relacionada com uma classe
▹ As colaborações revelam a cooperação entre classes/interação
entre objetos.
▹ As responsabilidades revelam métodos públicos.
▹ E algumas vezes atributos; por exemplo: “guarda coleções...”
30
Desenho de classes
▸ Desenho de classes
▹ As responsabilidades devem ser atribuídas/implementadas
seguindo os princípios do desenho de classes estudado
anteriormente:
▹ Coesão

▹ Acoplamento
▹ Desenho Orientado por responsabilidades
31
Coesão
▸ A coesão é o princípio Orientado a Objetos que visa garantir que uma classe é
concebida com um propósito único e bem focado.
▸ Quanto mais focada for uma classe, maior é a coesão dessa classe.
▸ Vantagens da alta coesão
▹ mais fáceis de manter (e mudam com menos frequência) do que as
classes com baixa coesão.
▹ Classes mais reutilizáveis do que outras classes.

32
Coesão
▸ Exemplo:

class Multiply {
Suponha que temos uma classe que multiplica
dois números, mas a mesma classe cria uma public int multiply(int num1, int num2)
{
janela pop-up que mostra o resultado. return num1 * num2;

▹ Este é o exemplo de classe de baixa coesão }


}

porque a janela e a operação de multiplicação


class Display {
não têm muito em comum. Para torná-lo public static void main(String[] args)
altamente coeso, teríamos que criar uma classe {
Multiply m = new Multiply();
Display e uma classe Multiply. Display irá System.out.println(m.multiply(5, 5));
chamar o método de Multiply para obter o }
}
resultado e mostra-lo. Desta forma,
desenvolvemos uma solução altamente coesiva.

33
Acoplamento
▸ Na orientação a objetos, o acoplamento se refere ao grau de conhecimento direto que
um elemento tem de outro. Em outras palavras, com que frequência as mudanças na
classe A forçam mudanças relacionadas na classe B.
▸ Existem dois tipos de acoplamento:
▹ Acoplamento rígido: em geral, o acoplamento rígido significa que as duas
classes frequentemente mudam juntas. Em outras palavras, se A sabe mais do
que deveria sobre a maneira como B foi implementado, então A e B estão
fortemente acoplados.
▹ Exemplo: se quiser mudar de pele, também terá que mudar de corpo
porque os dois estão unidos - eles estão fortemente acoplados.

34
Acoplamento
▸ Acoplamento fraco: o acoplamento fraco significa que as classes são em sua maioria
independentes. Se o único conhecimento que a classe A tem sobre a classe B é o que a
classe B expôs por meio da sua interface, então diz-se que a classe A e a classe B
estão fracamente acopladas.
▹ Exemplo: se quiser trocar de camisa, não será forçado a trocar de corpo -
quando se pode fazer isso, o acoplamento é fraco. Quando não se pode fazer
isso, tem um acoplamento forte.

35
Acoplamento
▸ Qual o melhor?
▹ Em geral, o acoplamento rígido é pior, porque
▹ reduz a flexibilidade e a reutilização do código,
▹ torna as mudanças muito mais difíceis,
▹ impede a capacidade de teste etc.
▹ O acoplamento fraco é melhor quando é necessário mudar ou fazer
crescer uma aplicação.
▹ apenas algumas partes da aplicação devem ser afetadas quando os
requisitos mudam.

36
“ Uma classe deve ter
apenas um motivo para
mudar.

Robert C. Martin - “Desenvolvimento Ágil de Software, Princípios, Padrões e


Práticas”.

37
PRINCÍPIO DE RESPONSABILIDADE
ÚNICA
▸ Conceito muito simples de explicar, porém difícil de implementar.
▸ No contexto do Princípio da Responsabilidade Única, a responsabilidade é definida como
um motivo para a mudança.
▸ Seguindo o princípio de responsabilidade única, garante-se que uma classe ou módulo
tenha alta coesão, o que significa que a classe não faz mais do que o que deveria fazer.
▸ Em suma, uma razão única para mudar.
▸ Se, pelo contrário, constrói-se uma classe com mais de uma responsabilidade, o que se
está a fazer é comprometer-se com essas responsabilidades.
▹ leva a um design que é frágil e difícil de manter, com tudo o que isso acarreta.
38
PRINCÍPIO DE RESPONSABILIDADE
ÚNICA

39
Desenho da interface das classes
▸ Desenho da interface das classes
▹ Depois de encontrar as classes, responsabilidades e colaboradores é
ainda necessário traduzir as descrições informais para chamada de
métodos e parâmetros dos mesmos.
▹ Repetir a análise de cenários tendo em conta agora as chamadas
de métodos, passagem de parâmetros e valores de retorno.
▹ Anotar as assinaturas dos métodos obtidas.
▹ Criar a estrutura das classes com os métodos públicos ainda sem o
código interno.
▹ Um desenho cuidadoso é a chave do sucesso da implementação.
40
Outros Aspetos do
desenvolvimento
Desenho de aplicações

41
Documentação
▸ Documentação

▹ Escrever os comentários das classes.

▹ Escrever os comentários dos métodos.

▹ Descrever claramente qual a finalidade das classes e métodos.

▹ A documentação nesta altura assegura que:

▹ O foco é no que faz e não em como se faz.

▹ E que isso não é esquecido.


42
Cooperação

▸ Trabalho de equipa

▹ O trabalho de equipa irá provavelmente ser a regra e não a exceção.

▹ A documentação é essencial para o trabalho de equipa.

▹ Um desenho orientado por objetos claro com componentes fracamente

acoplados suporta igualmente a cooperação.

43
Protótipos
▸ Prototipagem
▹ Devem-se construir protótipos da aplicação.
▹ Os protótipos são versões da aplicação onde uma parte é simulada para que se
possa experimentar com outras partes.
▹ Os protótipos ajudam no inicio na análise do sistema.
▹ Permitem uma identificação inicial do problema.
▹ Os componentes ainda incompletos podem ser simulados.
▹ Por exemplo retornando valores fixos.
▹ É de evitar comportamentos aleatórios que são difíceis de prever.

44
Desenvolvimento de software
▸ O modelo Waterfall
▹ Análise
▹ Design
▹ Implementação
▹ Testes unitários e de integração
▹ Instalação
▸ É o modelo de desenvolvimento mais conhecido e tradicional.
▹ Não é o mais usado.
▹ Parte do princípio que os programadores conhecem à partida todas as
funcionalidades em detalhe e que o sistema não será alterado depois de entregue.
45
Desenvolvimento de software
▸ O modelo Iterativo
▸ Baseia-se em prototipagem, interações com o cliente e pequenos ciclos de
desenvolvimento iterativos e incrementais
▹ Iterações com:
▹ Análise
▹ Design
▹ Protótipo
▹ Feedback do cliente
▸ É, talvez, o modelo de desenvolvimento mais comum atualmente.
▹ É um modelo mais realístico.
▹ O software vai crescendo em vez de ser desenhado.
46
Padrões de
Desenho
Desenho de aplicações

47
Utilização de padrões de desenho
▸ Tendo em conta que:
▹ As relações entre classes são bastante importantes e podem ser complexas.

▹ Algumas relações que se estabelecem entre classes ocorrem em várias aplicações.

▸ Existem padrões de desenho de classes que ajudam a identificar e


clarificar as relações entre classes
▹ Conhecidos como Software Design Patterns.

▹ São descrições de soluções recorrentes para problemas recorrentes.

▹ São problemas estudados com boas soluções que são exemplos de boas práticas no desenvolvimento de software.

▹ São descritos com base numa estrutura (tal como as cartas CRC) e que foi inicialmente usada no campo da arquitetura. 48
Estrutura dos padrões de desenho
▸ Software Design Patterns:
▹ Nome do padrão
▹ Problema abordado pelo padrão
▹ Como é proposta a solução
▹ Estrutura
▹ Participantes
▹ Colaborações
▹ Consequências da solução
▹ Resultados e compromissos
49
Padrão Singleton
▸ É um padrão criacional.
▹ Lida com o problema de existir apenas uma única instância de uma dada classe.
▸ Todos os clientes utilizam o mesmo objeto.
▸ A solução neste caso é tornar o construtor privado para impedir que se criem objetos
externamente.
▸ É obtida a única instância existente através de um método estático getInstance()
▸ Exemplo: A classe Canvas de um objeto Shape.

50
Exemplo –
Padrão Singleton

51
Padrão Decorator
▸ É um padrão estrutural.
▹ Lida com o problema de adicionar funcionalidade a um objeto existente sem utilizar a herança
de classes.
▸ Os clientes deste padrão requerem um objeto de um determinado tipo (uma interface ou uma
superclasse) mas com funcionalidades extra.
▸ A solução é criar um objeto que inclui (wraps) o objeto que se pretende utilizar e que será utilizado
em vez do original.
▹ O novo objeto inclui a funcionalidade que se pretende invocando-a diretamente no objeto
incluído e adicionalmente tem os métodos que aumentam a sua funcionalidade.
▸ Exemplo: A classe BufferedReader é um Decorator da classe Reader.
▹ Pode ser usada em vez da classe Reader porque inclui a mesma funcionalidade mas tem um
comportamento adicional ao da classe original.
52
Padrão Factory Method
▸ É um padrão criacional.
▹ Lida com o problema da criação de um objeto sem especificar concretamente qual a
classe que vai ser utilizada.
▸ Os clientes deste padrão requerem um objeto de um determinado tipo: de uma dada
interface ou superclasse.
▸ A solução é criar um método que tem liberdade para devolver um objeto de qualquer
classe que implemente essa interface ou que seja derivado dessa superclasse.
▹ O tipo exato do objeto depende do contexto.
▸ Exemplo: Os métodos iterator das classes de coleção.

53
Padrão Observer
▸ É um padrão comportamental.
▹ Lida com o problema de separar o modelo interno de uma vista desse modelo.
▸ Define uma relação de um para muitos entre objetos.
▸ O objeto observado notifica todos os objetos observadores de qualquer alteração no seu estado.
▸ Uma classe ou um conjunto de classes incluem a lógica e os dados do programa e permitem a existência de
observadores desses dados. Depois são criadas visualizações independentes da lógica que são atualizadas
sempre que mudam os dados.

▹ Podemos ter várias visualizações que ficam independentes dos dados.


▸ Exemplo: A classe SimulatorView do projeto Foxes-and-rabbits.
54
Padrão Observer
▸ Padrão Observer

55
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes &


Michael Kölling, Pearson Education Limited, 2016
▹ Capítulo 15

56
Programação Orientada por Objetos

Testes

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Testes

▸ Desenvolvimento dirigido pelos testes

2
A aplicação CityAssets

▸ Uma aplicação CityAssets que permita gerir os bens de uma

câmara Municipal
▸ Deve poder:
▹ Mostrar o número de bens
▹ Calcular o custo mensal de manutenção dos bens
▹ Apresentar a informação de um dado bem

3
Ideias para a solução...

▸ Uma classe para um bem

▸ Uma classe que guarda um conjunto de bens, tirando partido da Java

Collections Framework

▸ Uma classe que representa a câmara municipal

▸ Implementa-se esta solução e verifica-se se está tudo ok...

4
E se houver erros?

5
Como lidar com erros?

6
Processo de desenvolvimento

Modelo em cascata Modelo em espiral iterativo

Análise Determinar
objetivos

Desenho
Planear a Identificar e
Implementação iteração
seguinte
resolver
riscos

Verificação
Desenvolver
e testar
Evolução

7
Como garantir a qualidade do software

▸ Durante TODO o processo


▹ É fundamental
▹ Identificar corretamente os requisitos
▹ Desenhar uma boa solução
▹ Implementar cuidadosamente a solução Hoje, estamos sobretudo
preocupados com esta
▹ Verificar a solução parte, mas temos estado
atentos a todas as
restantes desde o início
▹ Fazer evoluir corretamente a solução do ano!

8
Várias técnicas para verificação da qualidade

▸ Revisão por pares da conceção (design), fonte (código) e outros entregáveis

▸ Verificação de qualidade com o auxílio de ferramentas

▹ Frequentemente integradas no IDE

▸ Realização de testes sobre o código desenvolvido

▸ ...

9
Testes unitários

▸ Focam-se numa parte específica da funcionalidade a que neste contexto vamos chamar
unidades
▹ Métodos
▹ Classes

▸ Cada teste unitário corresponde a um cenário de teste

▹ Tipicamente é implementado num método de teste

▸ Múltiplos cenários correspondem a múltiplos testes

▹ Estes testes podem ser agrupados em classes de teste


10
Testes unitários
▸ Porque gostamos de os fazer?
▹ Automáticos – podem ser chamados com simples clicks
▹ Escaláveis – tanto dão para programas pequenos como para sistemas com milhares de
classes (ou mais)
▹ Precisos – permitem identificar o ponto exato do programa que falhou
▹ Programam-se como o resto do nosso Código
▸ E, no entanto...
▹ Apenas testam unidades
▹ E a integração e colaboração entre essas unidades, não se testa?

11
Exemplo
Crie um caso de teste

▸ Criar um caso de teste é semelhante a criar


uma nova classe
▹ No BlueJ, clique no botão da direita em cima
do diagrama de classes e faça:
▹ New-> Classe
▹ Define o nome da classe com o sufixo Test
▹ Escolhe o tipo de classe Unit Test

O Junit já está incluído por defeito no BlueJ.


Não é o caso do Apache Netbeans.

13
Vista do projeto

▸ O BlueJ preenche a classe de teste


criada com a importação das
bibliotecas mecessárias e uma
estrutura com alguns métodos
14
Vamos criar o nosso primeiro teste
▸ Cenário:

▹ Criar um bem com um valor, um custo de manutenção e um número de 3 instâncias

▹ Testarseonúmerodeinstâncias do bem, depois de criado, é exatamente 3

▹ O teste é feito à custa de uma asserção do JUnit

▸ Nestes primeiros testes (e só nestes), vamos usar como cobaias construtores e

seletores.
▹ Na prática, como vamos ver mais a frente, nem sempre é necessário testar

seletores 15
Quando criamos um novo bem

16
Correr o teste
▸ Para verificar que ao número de bens criados é mesmo 1

Barra verde
significa que
todos os testes
passaram
(podem ser
milhares)

17
Experimente “estragar o teste”, admitindo que o
número de testes deveria ser 2

Barra vermelha
significa que pelo
menos um teste
falhou (podem
ser milhares)

18
O método assertEquals()

▸ O método estático org.junit.Assert.assertEquals


compara os seus dois argumentos, que podem ser:
▹ Valores de tipos primitivos (int, float, boolean, double, long,
short, char, byte)
▹ Objetos (de quaisquer tipos)
▸ Se forem iguais, o teste passa
▸ Se forem diferentes, o teste não passa

19
Outro teste...

@Test
public void testToString() {
Asset asset = new Asset(15000.00,155.55);
assertEquals("Capital 15000.00 € - custo mensal 155.55 €",asset.toString());
}

20
Construção do método de teste
▸ Criar o objeto e colocá-lo num estado conhecido
▸ Invocar um método que retorna o resultado “real”
▸ Construir o resultado esperado
▸ Invocar:
assertEquals(valorEsperado, valorReal)

▸ Se tudo estiver bem, o teste passa


Nota: para que isto funcione bem, temos de implementar o
método equals() na classe dos objetos a testar! 21
Quando especificar os testes?

▸ Antes de desenvolver o código a testar?

▸ Depois de desenvolver o código a testar?

22
Test-Driven Development - TDD

Desenvolvimento
dirigido pelos testes
Nova
Melhorar código
Desenvolvimento e voltar a testar
funcionalidade:
escrever um
teste para ela
dirigido pelos testes
Correr todos os Compilar. Erros:
testes. Todos o teste refere-
devem se a coisas que
(continuar a) ainda não
passar. existem

Escrever código
Corrigir erros de
que faz o teste
passar compilação

Correr todos os
testes. O teste
acabado de criar
deve falhar
24
Desenvolvimento dirigido pelos testes
▸ Os testes dirigem o processo
▹ Antes de acrescentar funcionalidade, escrever um teste
▹ Depois de falhar o teste, acrescentamos a funcionalidade
▸ O que ganhamos com este processo?
▹ Controlo sobre o processo de desenvolvimento
▸ Princípios fundamentais:
▹ Nunca estar a mais de um teste de distância da barra verde
▹ Nunca escrever novo teste quando se está com a barra vermelha
▹ Correr todos os testes frequentemente (pelo menos uma vez por manhã ou tarde, de
preferência várias)
25
Funcionamento dos testes de unidade

▸ Independência dos testes


▹ Ordem de execução dos testes deve ser sempre irrelevante
▸ Testar a interface, não a implementação
▹ Os testes devem ser criados a pensar na interface
▹ Nunca expor estado interno dum objeto para efeitos de teste
▹ Se necessário, enriqueça a interface – mesmo que isso implique criar operações públicas só para
facilitar os testes

▸ Não testar a plataforma


▹ A API do Java está bem testada, não deve ter de a testar
26
Desenvolvimento dirigido pelos testes
Test-Driven Development - TDD

Exemplo
A aplicação CityAssets
▸ Por uma questão de transparência para com os seus cidadãos, uma cidade
decide conceber um sistema informático que lhe permita representar
todos os bens (edifício, veículo,...) que possui.
▸ Um bem é definido pelo seu valor e custo mensal de manutenção. Também
queremos saber o número de instâncias de cada bem.
▸ Para o mini sistema de informação que estamos a desenvolver, uma
cidade pode ser considerada como uma classe de objetos que referencia
todos os bens que possui. Esta classe deve oferecer os seguintes serviços:
– Consultar a informação de um determinado imóvel, – Calcular o custo
mensal total de manutenção dos bens,
28
Comecemos pela classe Asset (Esqueleto)

▸ É de notar que as classes são construídas por incrementos.


public class Asset
{
private double value;
private double manutentionCost;
private int numberOfInstances;

public Asset(double value, double cost, int number) {}

public double getValue(){}

public double getManutentionCost(){}

public void setManutentionCost(double cost){}

public static int getNumberOfInstances(){}

public String toString(){}


}
29
Normalmente... Se seguir o que foi feito em IPOO

1. Começaria por identificar variáveis e constantes a usar

2. Especificaria o construtor, seguido dos restantes métodos

3. Testaria no fim, ou à medida que os métodos fossem ficando prontos

30
Regras específicas do TDD

▸ No desenvolvimento guiado pelos testes, tem de começar por especificar testes


antes de escrever o código

▸ Comecemos pelo construtor, dado que sem ele não vamos conseguir fazer mais

nada
▸ Acrescente um teste simples ao construtor
▹ Sim, esse mesmo, que ainda não desenvolveu

31
Vários erros de compilação

▸ Resultado expectável nesta fase

32
Na classe Asset, falta o construtor

▸ Lembre-se, para começar, só queremos compilar...

Para que o programa possa compilar,


garantimos que os métodos retornam um
valor neutro compátível com o tipo de
retorno.

33
Vamos testar...

▸ Naturalmente o teste dá erro

uma vez que o construtor ainda


não faz nada!
▹ Os seletores também não...

34
Transformemos o teste para ser mais geral
▸ Só podemos avançar para o próximo teste depois de correr com sucesso este

35
Vamos corrigir o código da classe Asset

▸ Sucesso, podemos avançar para um seletor do atributo manutentionCost

36
Primeiro, construímos o novo teste

37
Este código é suficiente para passar o teste (e errado, claro)

38
Temos de criar um teste em que o código não passe

@Test
public void testToString(){
Asset asset = new Asset(15000.0,155.55,1);

Asset asset2 = new Asset(6700.0,29.0,3);


assertEquals("Capital 15000.0 € - custo mensal 155.55 € - 1 Instância(s).",asset.toString());
assertEquals("Capital 6700.0 € - custo mensal 29.0 € - 3 Instância(s).",asset2.toString());
}

39
Agora, alteramos o código, para passar no teste

@Override
public String toString(){
return "" + "Capital " + value +
" € - custo mensal " + manutentionCost + " € - " +
numberOfInstances + " Instância(s).";
}

40
Testar o seletor do número de bens
@Test
public void testgetNumberOfInstances(){
asset = new Asset(6700.0,29.0,2);
assertEquals(2,asset.getNumberOfInstances());
}

41
Agora, alteramos o código, para passar no teste

public int getNumberOfInstances()


{
return numberOfInstances;
}

Agora, passa o teste

42
Definir uma fixture
public class AssetTest @Test
{ public void testToString(){
Asset asset, asset2; assertEquals("Capital 15000.0 € - custo mensal 155.55 € - 1
Instância(s).",asset.toString());
public AssetTest()
assertEquals("Capital 6700.0 € - custo mensal 29.0 € - 3
{
Instância(s).",asset2.toString());
asset = new Asset(15000.0,155.55);
}
asset2 = new Asset(6700.0,29.0);
}
@Test
public void testSimpleConstructor(){
public int getNumberOfInstances()
assertEquals(15000.0, asset.getValue());
{
assertEquals(155.55, asset.getManutentionCost());
return numberOfInstances;
}
}

43
Para quê fazer código incompleto até ter testes que o detectem?

▸ Queremos ter uma bateria de testes que cubra quer os casos


particulares, quer o caso geral
▸ É arriscado deixar funcionalidades por testar
▹ Queremos criar testes para todas as funcionalidades exigidas
▹ Devemos tentar tornar impossível que uma implementação fictícia possa
passar nos vários cenários de teste
▸ Portanto, só devemos acrescentar novo Código depois de ter um teste que falhe
na sua ausência

44
Podemos agora melhorar o código
▸ Atividade conhecida como refabricação (refactoring)
▹ Devemos aperfeiçoar código que esteja a funcionar
▹ Melhorando a sua estrutura interna
▹ Sempre sem alterar o seu comportamento externo
▹ Para quê?
▹ Tornar o código fonte tão fácil de compreender quanto possível
▹ Preparar código para facilitar adição de novas funcionalidades
▹ Mas isto não tem riscos?
▹ Pode haver regressões, i.e., podemos inadvertidamente introduzir bugs em código que estava a
funcionar bem
▹ Porisso, éfundamental ter testesquedetetemessesbugsque podemosinadvertidamenteintroduzir durante as
modificações

45
Algumas regras no desenvolvimento guiado por testes

▸ ⚠ Só refabricamos código que passa em todos os testes existentes


▸ ⚠ Só acrescentamos funcionalidades novas quando há um teste que o
código atual não passa
▹ Se queremos acrescentar uma nova funcionalidade, acrescentamos um novo
teste que “especifica” essa funcionalidade
▹ Assim, garantimos ter pelo menos um teste para cada nova funcionalidade
acrescentada

46
Exemplo de refabricação

▸ Consideramos que a aplicação necessita de um segundo


construtor que inicializa por defeito o número de bens a um. Uma
solução para este segundo construtor poderia ser:

public Asset(double value, double cost)


{
this.value = value;
this.manutentionCost = cost;
this.numberOfInstances = 1;
}
47
Exemplo de refabricação
public Asset(double value, double cost, int number)
{
this.value = value;
this.manutentionCost = cost; • Código repetido,
this.numberOfInstances = number; considerado um sinal
} de má programação

public Asset(double value, double cost)


{
this.value = value;
this.manutentionCost = cost;
this.numberOfInstances = 1;
}
public Asset(double value, double cost) { • Versão refabricada, com o construtor
simples a invocar o construtor mais
this(value, cost, 1); complexo
}

O teste da versão refabricada é executado e confirma


A correção do código refabricado.
48
Testar seletores

49
Vale a pena testar um seletor?
▸ Depende...
▹ Se o seletor se limita a retornar o valor de um campo, o teste é uma
perda de tempo
▹ Estes métodos podem ser simples demais para poder falhar por si só
@Override
public String getName() {
return this.name;
}

Pode testar estes métodos indiretamente!


Ø Recordar testes ao construtor

Ø Se o seletor faz algo mais complexo, vale a pena testar o seletor


50
Testes a seletores

▸ Começar por inicializar alguns objetos

▸ Invocar os seletores a testar e verificar se os resultados são os esperados

▸ Já vimos alguns exemplos, em testes anteriores

51
Testar modificadores

52
Vale a pena testar um modificador?
▸ Depende...
▹ Se o modificador se limita a afetar o valor de um campo, por cópia de um argumento, ou algo de
simplicidade equivalente, o teste pode ser uma perda de tempo
▹ Estes métodos podem ser simples demais para poder falhar por si só

@Override
public void setName(String name)
{
this.name = name;
}

Ø Em todo o caso, pode testar estes métodos indiretamente, recorrendo aos seletores
Ø Se o modificador faz algo mais complexo, vale a pena testar o modificador

53
Testar operações sobre coleções

▸ Operações que modificam a coleção

▸ Operações que visitam a coleção para recolher (acumular, filtrar, ...) informação

▸ Operações que comparam coleções

54
Operações que modificam a coleção

▸ Inserções (rever addAsset())

▸ Remoções (rever deleteAsset())

▸ Reordenações

55
Operações que visitam a
coleção para recolher
informação

56
Esquelete da classe Assets
/**
* Um conjunto de bens
*
* @author Cédric Grueau
public String getInfo(int indice)
* @version marco 2022
{
*/
}
import java.util.ArrayList;
public ArrayList<Asset> getAssets(){
public class Assets
}
{
ArrayList<Asset> assets;
public void removeAssets(Asset[] assets){
/**
}
* Constructor for objects of class Assets
public int size(){
*
* @param assets um array de bens
}
*/
}
public Assets(Asset[] assets)
{

}
57
Esquelete da classe Assets, versão “compilável”
/**
* Um conjunto de bens
*
* @author Cédric Grueau
public String getInfo(int indice)
* @version marco 2022
{
*/
return null;
}
import java.util.ArrayList;
public ArrayList<Asset> getAssets(){
public class Assets
return null;
{
}
ArrayList<Asset> assets;
public void removeAssets(Asset[] assets){
/**
* Constructor for objects of class Assets
}
*
public int size(){
* @param assets um array de bens
return 0;
*/
}
public Assets(Asset[] assets)
}
{

}
58
Teste de Assets(Asset[] assets)
public class AssetsTest
{
private Asset[] someAssets =
{new Asset(130.0,0.0,10),new Asset(3500.0,250.0,5),
new Asset(39000.0,1000.0,1)};
private Assets assets;
Começamos por declarar variáveis de instância para
os nossos testes.
public AssetsTest(){}

A anotação @Before indica que o método


@BeforeEach setup() deve ser executado antes de cada um
public void setUp(){ assets = new Assets(someAssets);} dos testes da classe AssetsTest.

@Test
public void testConstructor() {
System.out.println("Teste do construtor");
assertEquals(3, assets.size());
}
59
Teste à operação Assets(Asset[] assets)
▸ O teste falha, porque o
construtor e o método size
ainda tem a implementação por
omissão

public Assets(Asset[] assets){}

public int size(){


return 0;
}

60
Implementação de Assets(Asset[] assets)

public Assets(Asset[] assets)


{
this.assets = new ArrayList<Asset>();
for(Asset a : assets){
this.assets.add(a);
}
}

61
Implementação de size()

▸ O teste passa, após a


public int size(){
implementação do construtor e
return assets.size(); do método size
}

62
Teste de getInfo(int indice)

@Test
public void testGetInfo(){
assertEquals("Capital 3500.0 € - custo mensal 250.0 € - 5 Instância(s).",assets.getInfo(1));
}

▸ O teste falha, porque o


construtor e o método
getInfo() ainda tem a
implementação por omissão

63
Implementação de getInfo(int indice)

public String getInfo(int indice)


{
return assets.get(indice).toString();
}

64
Resumo
q Definição
q Processo de teste

65
Bibliografia

▸ Objects First with Java (6th Edition), David


Barnes & Michael Kölling, Pearson Education
Limited, 2016
▹ Capítulo 9

66
Instruções de afirmação do JUnit
▸ Boolean
▸ Assert Equals
▹ assertTrue(condition)
▹ assertEquals(expected, actual)
▹ assertFalse(condition)
▸ Assert Array Equals
▸ Null object
▹ assertArrayEquals(expected, actual)
▹ assertNull(object)
▹ assertEquals(expected[i],actual[i])
▹ assertNotNull(object)
▹ assertArrayEquals(expected[i],actual[i])
▸ Identical
▹ assertSame(expected, actual)
▹ assertNotSame(expected, actual)

67
Exemplos de utilização de instruções de afirmações

@Test
public void testAssert(){ //instruções de
assertEquals(string1,string2);
//Declaração de variáveis
String string1="Junit"; assertSame(string3, string4);

String string2="Junit"; assertNotSame(string1, string3);


String string3="test";
assertNotNull(string1);
String string4="test";
assertNull(string5);
String string5=null;
assertTrue(variable1<variable2);
int variable1=1;
int variable2=2;
assertArrayEquals(airethematicArrary1, airethematicArrary2);
int[] airethematicArrary1 = { 1, 2, 3 };
int[] airethematicArrary2 = { 1, 2, 3 };
}
// …

68
O que é o teste de software?

▸ O teste de software consiste na verificação dinâmica de que o

comportamento de um programa, de acordo com um conjunto finito de


casos de teste, escolhidos de modo apropriado de entre um conjunto
normalmente infinito de testes possíveis, cumpre o comportamento
esperado

69
Implicações da definição de teste

▸ O teste implica executar o programa com determinados inputs


▸ Normalmente, é impossível testar todas as situações possíveis, por
serem infinitas
▸ Podemos e devemos usar estratégias de desenho detestesparacriar bonscasosde
teste
▸ Dado um teste, temos de ser capazes de decidir se um resultado é ou não
aceitável
▹ Esta decisão é conhecida como o problema do oráculo

70
Processo de teste (Modelo em V estendido)
Execute acceptance
tests

Specify Execute
Requirements system tests
Requirements System/acceptance
review test plan & test cases
review/audit
Specify/Design
Code System/
acceptance tests Execute integration
Design
tests
Integration
Design review
test plan & test cases
review/audit
Specify/Design Code
Integration tests

Code Execute
unit tests
Unit
Code reviews test plan & test cases
review/audit
Specify/Design Code (source: I. Burnstein, pg.15)
Unit tests

71
Fases de teste – Teste Unitário

▸ Teste de unidades individuais, ou de grupos de unidades relacionadas


▸ Tipicamente, um tipo de teste sobre a API
▸ Tipicamente, da responsabilidade do programador
▸ Testes baseados em experiência, especificações e no Código
▹ Mas o programador pode seguir estratégias com provas dadas!
▸ O principal objetivo é detetar defeitos funcionais e estruturais na unidade a
testar
72
Processo de testes – Testes de integração
▸ Teste em que os componentes são combinados e testados para avaliar a
interação entre eles
▸ Normalmente, são da responsabilidade de uma equipa de testes independente de quem
programou os componentes

▸ Os testes são baseados numa especificação do Sistema


▹ Especificações técnicas, desenho do sistema
▸ O principal objetivo é detetar defeitos que ocorram devido a interações entre unidades
diferentes, e que sejam visíveis ao nível da interface

73
Fases de teste – Testes de sistema

▸ Testes realizados sobre um sistema completo, de modo a avaliar se o sistema está


de acordo com os requisitos para ele especificados
▹ Requisitos funcionais e não-funcionais (ex. Eficiência)
▸ Testes funcionais de caixa-negra, através da interface com o utilizador,
normalmente baseados em informação recolhida no documento de requisitos
▹ Estes testes ignoram completamente os detalhes de implementação do sistema
▸ Normalmente, são testes realizados por uma equipa independente

74
Fases de teste – Testes de Aceitação

▸ Testes formais destinados a determinar se um sistema satisfaz, ou não, um determinado


conjunto de critérios de aceitação, de modo a que o cliente possa decidir se deve ou não
aceitar o sistema
▸ Testes formais realizados para permitir que o utilizador, cliente, ou outra entidade
autorizada aceitam um sistema, ou um componente

▹ Tipicamente realizados pelo cliente


▹ Frequentemente os testes são baseados num documento de requisitos, ou no manual do
utilizador
▹ O principal motivo é verificar se os requisitos e expectativas são atingidos
75
Fases do teste – Testes de Regressão

▸ Repetição seletiva de testes de um sistema, ou de um

componente, para verificar que:


▸ As modificações feitas desde o último teste não provocaram
efeitos indesejados

▸ O sistema, ou componente, continuam a satisfazer os requisitos


especificados

76
Testes

▸ O mecanismo de testes pode demonstrar a

existência de defeitos, mas nunca a sua ausência

▸ No contexto de POO, ficamos à um nível superficial da

prática efetiva de testes de software

77
Programação Orientada por Objetos

Iteradores

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Iterar sobre uma coleção

▸ Remover elementos de uma coleção

▸ Método equals

▸ Exemplo da agenda de contatos

2
A aplicação PlayList - continuação

▸ Uma aplicação CityAssets que permita gerir os bens de uma câmara

Municipal
▸ Deve poder:
▹ Mostrar o número de bens
▹ Calcular o custo mensal de manutenção dos bens
▹ Apresentar a informação de um dado bem

3
Implementação parcial da classe Assets
import java.util.ArrayList;

public class Assets


{
ArrayList<Asset> assets;
public ArrayList<Asset> getAssets(){
}
public Assets(Asset[] assets)
{
public void removeAssets(int number){
this.assets = new ArrayList<Asset>();
for(Asset a : assets){
}
this.assets.add(a);
}
}
}

public String getInfo(int indice)


{
return assets.get(indice).toString();
}

public int size(){


return assets.size();
}
4
Teste de removeAssets(Asset[] assets)
public class AssetsTest
{
private static final Asset[] SOME_ASSETS = {new Asset(130.0,0.0,10),new Asset(3500.0,250.0,5),
new Asset(39000.0,1000.0,1)};
private static final Asset[] ASSETS_TO_REMOVE = { Declaramos variáveis de instância para o novo teste.
new Asset(130.0,0.0,10),new Asset(39000.0,1000.0,1)};
Assets assets;
Executamos o teste: falha pela falta de
implementação do método removeAssets

@Test
public void testRemoveAssets() {
System.out.println("Teste de remoção de bens");
assets.removeAssets(ASSETS_TO_REMOVE);
assertEquals(1, assets.size());
}

5
Implementação de removeAssets(Asset[] assets)

▸ Como definir que dois bens são idênticos?


▹ == serve para verificar se os dois objetos têm a mesma referência
▸ Como definir o método equals(),tal como usamos até agora com o tipo
String?
▹ var1.equals(var2)
▹ asset1.equals(asset2)

public boolean equals(Object obj){


return this == obj; É suficiente?
}
6
Método equals()
public boolean equals(Object obj) {
if (this == obj) {
return true; // Igualdade de referência.
}
if (!(obj instanceof Asset)) {
return false; // Não é o mesmo tipo.
}
// Aceder aos campos do outro bem para verificar o estado.
Asset other = (Asset) obj;
return value == other.value &&
manutentionCost == other.manutentionCost &&
numberOfInstances == other. numberOfInstances;
}
7
Testar o método equals()
@Test
public void testEquals(){
Asset asset3 = new Asset(6700.0,29.0,3);
assertEquals(asset3,asset2);

8
Implementação de removeAssets(Asset[] assets)

public void removeAssets(Asset[] assets){


for(int i = 0; i < assets.length; i++){
for(int j = 0; j < this.assets.size(); j++){
if (assets[i].equals(this.assets.get(j))){
this.assets.remove(this.assets.get(j));
}
}
}
}

▸ Não se deve eliminar elementos de uma coleção


desta forma. Não é seguro.
9
Percorrer coleções com Iteradores
▸ Os objetos do tipo Iterator são utilizados para percorrer os vários elementos de uma coleção.
▹ É possível obter estes objetos através do método iterator() existente na coleção.
Todas as coleções do Java têm este método.
▹ O objeto iterator possui três métodos:
▹ hasNext() – diz se existe o próximo elemento.
▹ next() – devolve o próximo elemento
▹ remove() – remove o último elemento devolvido. Algumas coleções não
permitem a utilização deste método.
▹ Tal como nas coleções o tipo Iterator é genérico necessitando da informação de
qual é o tipo que se está a iterar (o mesmo que é usado como elemento da coleção)
▹ Ex: Iterator<Student> iterator
10
Percorrer coleções com Iteradores
▸ Utilização do objeto Iterator
Retorna o objeto Iterator

em java.util.Iterator Iterator<TipoDoElemento> iterator = nomeColecao.iterator();

while(iterator.hasNext()) {

chamar iterator.next() para obter o próximo objeto

usar o objeto
}

11
Percorrer coleções com Iteradores
▸ Funcionamento do iterador
myList.iterator()
myList:List

:Element :Element :Element :Element

Inicialmente é como se o
:Iterator iterador estivesse antes do
primeiro elemento da lista.

12
Percorrer coleções com Iteradores
▸ Funcionamento do iterador myList.iterator()
myList:List hasNext()?
:Element
next()

:Element :Element :Element

Vai buscar o
:Iterator próximo elemento e
avança o iterador
Element e = iterator.next();
13
Percorrer coleções com Iteradores
▸ Funcionamento do iterador myList.iterator()
myList:List hasNext()?
:Element
next()

:Element :Element :Element

:Iterator

Element e = iterator.next();
14
Percorrer coleções com Iteradores
▸ Funcionamento do iterador myList.iterator()
myList:List hasNext()?
:Element
next()

:Element :Element :Element

:Iterator

Element e = iterator.next(); 15
Percorrer coleções com Iteradores
▸ Funcionamento do iterador myList.iterator()
myList:List hasNext()?
:Element
Não existem
mais
elementos.
:Element :Element :Element Terminou a
operação.

:Iterator

16
Implementação de removeAssets(Asset[] assets)

▸ Com um iterador

public void removeAssets(Asset[] assetsToRemove){


Iterator<Asset> it = this.assets.iterator();
while(it.hasNext()){
Asset asset = it.next();
for(int i = 0; i < assetsToRemove.length; i++){
if (asset.equals(assetsToRemove[i])){
it.remove();
}
}
}
}

17
Índices versus Iteradores
▸ Formas de percorrer (iterar) uma coleção
▹ Ciclo for
▹ Usado quando é útil usar os índices ao percorrer-se a coleção.
▹ Usado em repetições que não envolvem coleções e que se repetem um determinado número de vezes
▹ Ciclo for-each
▹ Usado quando se pretende percorrer todos os elementos da coleção.
▹ Ciclo while
▹ Usado para percorrer os elementos da coleção quando se pretende interromper a meio.
▹ Usado em repetições que não envolvem coleções e em que não se sabe determinar o número de vezes que se
irá repetir.
▹ Objeto iterator
▹ Usado para percorrer os elementos da coleção quando se pretende interromper a meio.
▹ Usado em coleções que não usam índices
▹ Usado quando se pretendem remover elementos da coleção
18
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes &


Michael Kölling, Pearson Education Limited, 2016
▹ Capítulo 4

19
Programação Orientada por Objetos

Introdução às
Coleções
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Exemplo Monitorização de animais

▸ Processamento funcional de coleções

▸ Expressões lambda

2
Exemplo – Monitorização de animais
▸ Requisitos da aplicação:
▹ Sistema de monitorização de populações animais.
▹ Processa relatórios dos avistamentos de animais enviados
por vários observadores.
▹ Cada relatório consiste no nome e número de animais
avistados, quem enviou o relatório (um inteiro), a área da
observação (um inteiro) e a altura em que foi feita a
observação (um inteiro representando o número de dias
desde o inicio da monitorização)

3
Exemplo – Monitorização de animais

4
Exemplo – Monitorização de animais
▸ Classes da aplicação:
▹ Sighting
▹ Relatório da observação.
▹ SightingReader
▹ Usada para ler uma lista de
observações a partir de um ficheiro.
▹ AnimalMonitor
▹ Faz o processamento de relatórios de
observações.

5
Exemplo – Monitorização de animais
▸ Representação da
public class Sighting {
private String animal;
observação – classe
private int spotter; // observador
private int count;
private int area;
Sighting private int period;

public Sighting(String animal, int spotter,


int count, int area, int period) {
this.animal = animal;
this.spotter = spotter;
this.count = count;
this.area = area;
this.period = period;
}
// restantes métodos
}

6
Exemplo – Monitorização de animais
public String getAnimal() {
return animal;
}

public int getSpotter() {


▸ Classe Sighting – }
return spotter;

métodos seletores get public int getCount() {


return count;
}

public int getArea() {


return area;
}

public int getPeriod() {


return period;
}

7
Exemplo – Monitorização de animais

public String getDetails() {


▸ Classe Sighting –
método getDetails return animal +
", count = " + count +
", area = " + area +
", spotter = " + spotter +
", period = " + period;
}
Podia ter sido
chamado
toString

8
Exemplo – Monitorização de animais

▸ Obtenção de observações a public class SightingReader {

partir de ficheiro – classe public SightingReader() {


}
SightingReader
public ArrayList<Sighting> getSightings(String filename)
{

// Devolve uma lista de observações


// lida a partir de um ficheiro.
}

Não precisamos de
saber os detalhes de
implementação
9
Exemplo – Monitorização de animais
public class AnimalMonitor {

▸ Processamento das
private ArrayList<Sighting> sightings;

public AnimalMonitor() {
observações – classe this.sightings = new ArrayList<>();
}
AnimalMonitor
public void addSightings(String filename) {
SightingReader reader = new SightingReader();
sightings.addAll(reader.getSightings(filename));
}

// restantes métodos
Lê a lista de }
observações e
adiciona-a à lista
sightings

10
Exemplo – Monitorização de animais

▸ Classe AnimalMonitor public void ????????() {

– método ???????? for(Sighting record : sightings) {


System.out.println(record.getDetails());
}

O que faz?

11
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor public void printList() {
– método printList
for(Sighting record : sightings) {
System.out.println(record.getDetails());
}

Mostra todas as
observações

12
Exemplo – Monitorização de animais

▸ Classe AnimalMonitor public void ????????(String animal) {


– método ????????
for(Sighting record : sightings) {
if(animal.equals(record.getAnimal())) {
System.out.println(record.getDetails());
}
}
}
O que faz?

13
Exemplo – Monitorização de animais

▸ Classe AnimalMonitor public void printSightingsOf(String animal) {

– método
for(Sighting record : sightings) {
printSightingsOf
if(animal.equals(record.getAnimal())) {
System.out.println(record.getDetails());
}
}
}
Mostra apenas as
observações de
um dado animal

14
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor
– método ???????? public void ????????(int spotter) {

for(Sighting record : sightings) {


if(record.getSpotter() == spotter) {
System.out.println(record.getDetails());
}
}
}
O que faz?

15
Exemplo – Monitorização de animais

▸ Classe AnimalMonitor public void printSightingsBy(int spotter) {


– método
printSightingsBy for(Sighting record : sightings) {
if(record.getSpotter() == spotter) {
System.out.println(record.getDetails());
}
}
}

Mostra as
observações feitas
por um dado
observador
16
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor public int ????????(String animal) {
– método ???????? int total = 0;
for(Sighting sighting : sightings) {
if(animal.equals(sighting.getAnimal())) {
total = total + sighting.getCount();
}
}
return total;
}

O que faz?

17
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor
public int getCount(String animal) {
– método getCount
int total = 0;
for(Sighting sighting : sightings) {
if(animal.equals(sighting.getAnimal())) {
total = total + sighting.getCount();
}
}
Retorna o total de return total;
observações de }
um dado animal

18
Exemplo – Monitorização de animais

▸ Classe AnimalMonitor public void ????????(ArrayList<String> animalNames,


int dangerThreshold) {
– método ????????
for(String animal : animalNames) {
if(getCount(animal) <= dangerThreshold) {
System.out.println(animal + " is endangered.");
}
}
}

O que faz?

19
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor
– método public void printEndangered(ArrayList<String> animalNames,
int dangerThreshold) {
printEndangered
for(String animal : animalNames) {
if(getCount(animal) <= dangerThreshold) {
System.out.println(animal + " is endangered.");
}
}

Mostra todos os animais }


cujas observações tinham
um número de
avistamentos abaixo de
um dado limiar

20
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor
public void ????????() {
– método ????????
Iterator<Sighting> it = sightings.iterator();
while(it.hasNext()) {
Sighting record = it.next();
if(record.getCount() == 0) {
it.remove();
}
}
}
O que faz?

21
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor public void removeZeroCounts() {

– método
Iterator<Sighting> it = sightings.iterator();
removeZeroCounts
while(it.hasNext()) {
Sighting record = it.next();
if(record.getCount() == 0) {
it.remove();
}
Elimina todos os }
relatórios cujo }
número de animais
avistados é 0.

22
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor public ArrayList<Sighting> ????????(String animal, int area) {

– método ???????? ArrayList<Sighting> records = new ArrayList<>();


for(Sighting record : sightings) {
if(animal.equals(record.getAnimal())) {
if(record.getArea() == area) {
records.add(record);
}
}
}
return records;
}
O que faz?

23
Exemplo – Monitorização de animais
public ArrayList<Sighting> getSightingsInArea(String animal, int area) {
▸ Classe AnimalMonitor – ArrayList<Sighting> records = new ArrayList<>();

método for(Sighting record : sightings) {

getSightingsInArea if(animal.equals(record.getAnimal())) {
if(record.getArea() == area) {
records.add(record);
}
}
}
return records;
Retorna uma lista de
observações de um }
animal especificado
numa dada área.

24
Exemplo – Monitorização de animais

▸ Classe AnimalMonitor public ArrayList<Sighting> ????????(String animal) {


– método ???????? ArrayList<Sighting> filtered = new ArrayList<>();
for(Sighting record : sightings) {
if(animal.equals(record.getAnimal())) {
filtered.add(record);
}
}
return filtered;
}
O que
faz?

25
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor public ArrayList<Sighting> getSightingsOf(String animal) {
– método
getSightingsOf ArrayList<Sighting> filtered = new ArrayList<>();
for(Sighting record : sightings) {
if(animal.equals(record.getAnimal())) {
filtered.add(record);
}
}
Filtra as observações, return filtered;
retornando uma lista apenas }
com os relatórios de um
dado animal.

26
Processamento Imperativo de Coleções
▸ Processamento imperativo de coleções
▹ Todos os processamentos de coleções feitos até agora utilizaram um paradigma de
programação imperativo.
▹ Consistiam num ciclo que ia obter cada um dos elementos da coleção e efetuava
uma determinada ação com esse elemento.
A estrutura deste processamento era semelhante a:

ciclo (para cada elemento da coleção)


obter o elemento
fazer algo com o elemento
fim do ciclo

O ciclo podia ser qualquer um: while, do-while, for, for-each


27
Processamento Funcional de Coleções
▸ Processamento funcional de coleções
▹ Em muitos casos o processamento funcional de coleções é mais prático, conciso
e expressivo do que o processamento imperativo. O Java adotou este tipo de
processamento a partir da versão 8.
▹ O conceito de lambda está na base do funcionamento do processamento funcional:
▹ A interpretação mais simples é que é possível passar como parâmetro de um
método um pedaço de código que o método pode executar depois quando
necessitar. Anteriormente apenas passámos para métodos, valores de tipos
primitivos e objetos.

28
Processamento Funcional de Coleções

▸ Processamento funcional de coleções


▹ Processamento de coleções com expressões lambda:
▹ A abordagem usada com expressões lambda é algo como: “Faz isto a
cada elemento da coleção”, onde “isto” representa um pedaço de
código. A estrutura deste processamento é do tipo:

Coleção.fazIstoParaCadaElemento(pedaço de código)

▹ O código fica mais simples.


▹ O ciclo parece ter desaparecido. 29
Expressões Lambda
▸ Sintaxe de uma expressão lambda
▹ As expressões lambda são parecidas com os métodos mas, ao contrário dos métodos, não têm
de pertencer a uma classe e não necessitam de um nome.
▹ Exemplo
Método para mostrar os detalhes de um relatório de avistamento

public void printSighting(Sighting record) {


System.out.println(record.getDetails());
}

Expressão lambda equivalente ao método anterior

(Sighting record) -> {


System.out.println(record.getDetails());
}
30
Expressões Lambda
▸ Sintaxe de uma expressão lambda
▹ Exemplo: Expressão lambda equivalente ao método anterior

(Sighting record) -> {
System.out.println(record.getDetails());
}

▹ Sem o modificador public ou private


▹ Sem tipo de retorno
▹ O compilador consegue saber o tipo de retorno pelo que é retornado do método
▹ Sem nome do método
▹ Começa logo com a lista de parâmetros
▹ Uma seta ( -> ) separa a lista de parâmetros do corpo do lambda
31
Expressões Lambda
▸ Expressões lambda
▹ São usadas habitualmente para tarefas simples que não requerem a complexidade de
uma classe.
▹ São também conhecidas for funções anónimas (ou métodos anónimos)
▹ Utilização em coleções – o método forEach: Coleção.fazIstoParaCadaElemento(pedaço de código)

myList.forEach(… Código que se aplica a cada elemento da coleção …)

O parâmetro deste método é uma expressão lambda


32
Expressões Lambda Parece
mais
complexo!
public void printList() {
▸ Expressões lambda – for(Sighting record : sightings) {

Exemplo com o método }


System.out.println(record.getDetails());

printList: }
// Com expressões lambda:
public void printList() {
sightings.forEach(
(Sighting record) -> {
System.out.println(record.getDetails());
Já não aparece }
o ciclo
);
}

33
Expressões Lambda Simplificação
1

▸ Expressões lambda – Exemplo //Com expressões lambda:

com o método printList:


public void printList() {
sightings.forEach(
(record) -> {

Simplificações System.out.println(record.getDetails());
}
1. O tipo do parâmetro pode ser
);
omitido uma vez que o
compilador consegue }
determiná-lo: é o mesmo que
o tipo dos elementos da
coleção.

34
Expressões Lambda
▸ Expressões lambda – Exemplo
com o método printList: // Com expressões lambda:
Simplificação
public void printList() {
2

sightings.forEach(
record -> {
Simplificações System.out.println(record.getDetails());

2. Se existir apenas um }
);
parâmetro não é
necessário colocar os }
parênteses.

35
Expressões Lambda
▸ Expressões lambda – Exemplo
Simplificação
com o método printList: // Com expressões lambda:
3
public void printList() {
sightings.forEach(
record -> System.out.println(record.getDetails())
Simplificações );

3. Se existir apenas uma }

instrução no corpo do
lambda não é necessário
usar chavetas, nem colocar
o ponto e vírgula final.

36
Expressões Lambda
public void printList() {
▸ Expressões lambda – for(Sighting record : sightings) {
Exemplo com o método System.out.println(record.getDetails());
}
printList:
}
// Com expressões lambda: Mais simples!
public void printList() {
sightings.forEach(record ->
System.out.println(record.getDetails()));
}

37
Expressões Lambda
Símbolo ->
▸ Sintaxe de uma expressão
lambda
record -> System.out.println(record.getDetails())

Parâmetro(s) de
entrada(s) Instrução(ões) para execução

38
Streams
▸ Conceito de stream
▹ O java 8 introduziu o conceito alargado de stream que é entendido como uma coleção mas com
características especiais:
▹ Os elementos de uma stream não são acedidos por um índice mas sim sequencialmente.
▹ O conteúdo e a ordenação de uma stream não pode ser alterado.
▹ Neste caso ter-se-ia que criar uma nova stream
▹ Uma stream pode ser potencialmente infinita!
▹ Por exemplo o processamento de mensagens oriundas de uma rede social
▹ É utilizado para unificar o processamento de conjuntos de dados independentemente da sua origem.
▹ O método forEach da classe ArrayList é um exemplo de um método que utiliza a noção de
stream.

39
Streams
▸ Conceito de stream
▹ Algumas classes como a classe ArrayList, e outras classes de coleção,
disponibilizam um método stream que retorna uma stream com os
elementos da coleção por ordem dos seus índices.
▹ As operações habituais em coleções podem ser feitas através de streams
sem necessidade de utilizar ciclos. Estas operações utilizam expressões
lambda.
▹ Iremos estudar as operações de filtragem (filter), mapeamento (map)
e redução (reduce) utilizadas em steams.

40
Streams: o método filter
▸ O método filter
▹ Este método recebe uma stream,
seleciona alguns dos seus
elementos e cria uma nova stream
apenas com os elementos
selecionados
▹ Diz-se que aplicamos um filtro à
stream. Um exemplo poderia
ser a seleção de todos os
relatórios do animal “elefante”.
41
Streams: o método map
▸ O método map
▹ Este método recebe uma stream e cria
uma nova stream onde os elementos
iniciais são mapeados (transformados)
noutros elementos
▹ Um exemplo poderia ser substituir
cada objeto Sighting da coleção inicial
pelo número de animais observados
em cada um deles.

42
Streams: o método reduce
▸ O método reduce
▹ Este método recebe uma stream e reduz
todos os elementos a um único valor.
▹ Um exemplo poderia ser termos o
número de todos os elefantes
observados e depois somarmos tudo.
É um valor único com a soma de
todos.

43
Streams: pipelines
▸ Pipelines
▹ As streams e os seus métodos tornam-se mais úteis se os pudermos juntar como uma
sequência de operações. É esse o conceito de pipelines.
▹ Por exemplo queremos saber quantos avistamentos de elefantes foram registados:
1. Filtramos a coleção de relatórios para obtermos apenas os relatórios
respeitantes a elefantes.
2. Mapeamos cada relatório obtido (um objeto da classe Sighting) no número de
animais que está nesse relatório.
3. Reduzimos a um único valor que corresponde à soma de todos os números de
animais obtidos anteriormente.

44
Streams: pipelines
▸ Pipelines
▹ Quantos avistamentos de elefantes foram registados:
1. Filtramos a coleção de relatórios para obtermos apenas os relatórios respeitantes a elefantes.
2. Mapeamos cada relatório obtido (um objeto da classe Sighting) no número de animais que está nesse relatório.
3. Reduzimos a um único valor que corresponde à soma de todos os números de animais obtidos anteriormente.

45
Streams: o método filter
Método filter
▸ Cria um subconjunto (filtrado) da coleção
inicial. public void printSightingsOf(String animal) {

▸ Normalmente existe uma condição que


sightings.stream()
Obtém a stream
deve ser verdadeira para um dado
elemento para que este seja selecionado. .filter(sighting -> animal.equals(sighting.getAnimal()))
Esta condição é dada na forma de uma
.forEach(sighting -> System.out.println(sighting.getDetails()));
expressão lambda.
▹ Exemplo: Mostrar todos os }

relatórios de um determinado Filtra a stream


animal
Mostra os relatórios obtidos

46
Streams: o método filter
Método filter

▸ Cria um subconjunto (filtrado) da coleção


inicial. public void printSightingsOf(String animal) {

▸ Normalmente existe uma condição que sightings.stream() Obtém a stream

deve ser verdadeira para um dado .filter(sighting -> animal.equals(sighting.getAnimal()))

elemento para que este seja selecionado.


.forEach(sighting -> System.out.println(sighting.getDetails()));
Esta condição é dada na forma de uma
}
expressão lambda.
Filtra a stream
▹ Exemplo: Mostrar todos os Mostra os relatórios obtidos
relatórios de um determinado
animal
47
Streams: o método map
Obtém a stream
O método map
public void printSightingsBy(int spotter) {
▸ Cria uma nova stream onde os
Filtra a stream
sightings.stream()
elementos iniciais são mapeados
.filter(sighting -> sighting.getSpotter() == spotter)
(transformados) noutros elementos
.map(sighting -> sighting.getDetails())
▹ Exemplo mostrar os relatórios
.forEach(details -> System.out.println(details));
de um dado observador
obtendo primeiro todos os }

relatórios (mapeamento do Obtém os textos dos relatórios Mostra os textos obtidos


relatório no seu texto
descritivo.
Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
48
Streams: o método reduce
O método reduce
▸ Recebe uma stream e reduz todos public int getCount(String animal) {
os elementos a um único valor. Obtém a stream
return sightings.stream()
▹ Exemplo. Contar todos os
animais de um determinado .filter(sighting -> animal.equals(sighting.getAnimal()))

tipo. .map(sighting -> sighting.getCount())


Filtra a stream

.reduce(0, (runningSum, count) -> runningSum + count);

Obtém o número de
avistamentos por relatório Soma os todos os números de
avistamentos

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
49
Streams: o método reduce
O método reduce
▸ Recebe uma stream e reduz todos
os elementos a um único valor. public int getCount(String animal) {

▹ Exemplo. Contar todos os return sightings.stream()

animais de um determinado .filter(sighting -> animal.equals(sighting.getAnimal()))


tipo.
.map(sighting -> sighting.getCount())

.reduce(0, (runningSum, count) -> runningSum + count);

Os parâmetros do método }
reduce são um pouco mais
complicados!

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
50
Streams: o método reduce
O método reduce
▸ Recebe uma stream e reduz todos os elementos a um único valor.
▹ Exemplo. Contar todos os animais de um determinado tipo.

return sightings.stream()

.filter(sighting -> animal.equals(sighting.getAnimal()))

.map(sighting -> sighting.getCount())

.reduce(0, (runningSum, count) -> runningSum + count);

Valor inicial Valor “acumulado” Valor do elemento Resultado a “acumular”

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
51
Exemplo – Monitorização de animais
▸ Processamento funcional das observações – nova classe AnimalMonitor

public class AnimalMonitor {


private ArrayList<Sighting> sightings;
public AnimalMonitor() {
this.sightings = new ArrayList<>();
}
public void addSightings(String filename) {
SightingReader reader = new SightingReader();
sightings.addAll(reader.getSightings(filename));
}
// restantes método
}

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
52
Exemplo – Monitorização de animais
Classe AnimalMonitor – método public void printList() {
printList for(Sighting record : sightings) {
System.out.println(record.getDetails());
}
}

Nova classe AnimalMonitor – método public void printList() {


printList sightings.forEach(record -> System.out.println(record.getDetails()));
}

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
53
Exemplo – Monitorização de animais
Classe AnimalMonitor – método public void printSightingsOf(String animal) {
printSightingsOf for(Sighting record : sightings) {
if(animal.equals(record.getAnimal())) {
System.out.println(record.getDetails());
}
}
}
public void printSightingsOf(String animal) {
sightings.stream()
Nova classe AnimalMonitor – método
.filter(sighting -> animal.equals(sighting.getAnimal()))
printSightingsOf .forEach(sighting -> System.out.println(sighting.getDetails()));
}

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
54
Exemplo – Monitorização de animais
Classe AnimalMonitor – método public void printSightingsBy(int spotter) {
for(Sighting record : sightings) {
printSightingsBy if(record.getSpotter() == spotter) {
System.out.println(record.getDetails());
}
}
}

public void printSightingsBy(int spotter) {


sightings.stream()
Nova classe AnimalMonitor – método .filter(sighting -> sighting.getSpotter() == spotter)
.map(sighting -> sighting.getDetails())
printSightingsBy .forEach(details -> System.out.println(details));
}

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
55
Exemplo – Monitorização de animais
public int getCount(String animal) {
Classe AnimalMonitor – int total = 0;
for(Sighting sighting : sightings) {
método getCount if(animal.equals(sighting.getAnimal())) {
total = total + sighting.getCount();
}
}
return total;
}

public int getCount(String animal) {


return sightings.stream()
.filter(sighting -> animal.equals(sighting.getAnimal()))
.map(sighting -> sighting.getCount())
.reduce(0, (runningSum, count) -> runningSum + count);
}

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
56
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor –
método removeZeroCounts public void removeZeroCounts() {
Iterator<Sighting> it = sightings.iterator();
while(it.hasNext()) {
Sighting record = it.next();
if(record.getCount() == 0) {
Também é possível
it.remove();
com coleções usando o
método removeIf }
parecido com o }
forEach. }

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
57
Exemplo – Monitorização de animais
▸ Classe AnimalMonitor
– método public void removeZeroCounts() {
removeZeroCounts sightings.removeIf(sighting -> sighting.getCount() == 0));

Condição de
remoção do
elemento

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
58
Streams
▸ Streams
▹ Alguns dos métodos usados pelas streams em pipelines são considerados intermediários,
enquanto outros são terminais.
▹ Os métodos intermediários devolvem streams e podem ser seguidos por outros métodos.
▹ Por exemplo map e filter.
▹ Os métodos terminais não podem ser seguidos por outros métodos de stream porque não
devolvem streams ou porque não devolvem nada (são void).
▹ Por exemplo reduce.
▹ As streams têm mais métodos para além dos estudados aqui (filter, map e reduce) que podem
ser consultados na documentação oficial e que pemitem outras operações.
▹ Por exemplo count, limit, findfirst, skip, etc.:
59
Operador de resolução de escopo em Java

▸ O operador (::) em Java é conhecido como operador de referência de método ou


operador de dois pontos.
▸ É usado para chamar um método fazendo referência a ele com a ajuda de sua classe
diretamente. Podemos usar o operador de referência de método em vez de expressões
lambda porque ele se comporta da mesma forma que as expressões lambda.
▸ A única diferença entre a expressão lambda e o operador de referência de método é que,
em vez de fornecer um delegado ao método, ele usa uma referência direta ao método
pelo nome.

60
Operador de referência de método em java

Alvo da referência Nome do método


▸ O alvo da referência é colocado
antes do operador (::) e o nome
System.out :: println
do método é escrito depois.

operador de referência de método

61
Operador de referência de método: exemplo

import java.util.stream.*;
public class LambdaExpressionExample {

public static void main(String[] args) {

Stream<String> st = Stream.of("Lisboa", "Porto", "Évora", "Faro", "Aveiro", "Braga");


st.forEach(city -> System.out.println(city));
}
}

▸ Alternativa

public static void main(String[] args){

Stream<String> st = Stream.of("Lisboa", "Porto", "Évora", "Faro", "Aveiro", "Braga");


st.forEach(System.out::println);
}
62
Tipos de Referências de Método
▸ Existem quatro tipos de referências de método em Java:
Tipos Descrição Sintaxe Exemplo

Referência a um método estático É usado para referenciar Math::floor é


um método estático de uma ContainingClass::staticMethodName equivalente a
classe. Math.floor(x)

Referência a um método de Refere-se a um método de System.out::println é


instância de um objeto específico instância usando uma containingObject::instanceMethodName
equivalente a
referência a um objeto System.out.println(x)
fornecido.
Referência a um método de Chama o método de String::indexOf é
instância de um objeto arbitrário instância numa referência ContainingType::methodName
equivalente a
de um determinado tipo a um objeto fornecido pelo str.indexOf()
contexto.
Referência a um construtor Faz referência a um LinkedList::new é
construtor. ClassName::new equivalente a new
LinkedList()
63
Bibliografia

▸ Objects First with Java (6th Edition), David


Barnes & Michael Kölling, Pearson Education
Limited, 2016
▹ Capítulo 5

Prof. José Cordeiro – Prof. José Sena Pereira – Prof. Cédric Grueau
64
Programação Orientada por Objetos

Herança

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Herança de classes
▸ Hierarquias de classes

▸ Principio da substituição

2
Exemplo – Rede Social
▸ Requisitos da rede social:
▹ Um pequeno protótipo com a base para o armazenamento e
apresentação de mensagens.

▹ Faz o armazenamento de mensagens de texto e mensagens de


imagem.

▹ As mensagens de texto podem ter várias linhas;

▹ as mensagens de imagem têm uma imagem e uma descrição.

▹ Todas as mensagens devem incluir o seu autor, a altura em que foi


enviada, o número de “gostos” e a lista de comentários.

3
Exemplo – Rede Social
▸ Objetos que pretendemos representar: MessagePost e PhotoPost

4
Exemplo – Rede Social
▸ Classes associadas: MessagePost e PhotoPost

Atributos

Métodos

5
Exemplo – Rede Social
▸ Diagrama de objetos da rede social

6
Exemplo – Rede Social
▸ Diagrama de classes da rede social

7
Exemplo – Rede Social
▸ Classe MessagePost public class MessagePost {

private String username;


private String message;
private long timestamp;
private int likes;
private ArrayList<String> comments;

public MessagePost(String author, String text) {


username = author;
message = text;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}

// continua…
currentTimeMillis ???

8
(System.currentTimeMillis – das bibliotecas do Java)

9
Exemplo – Rede Social
▸ Métodos da classe public void like() {
likes++;
}
MessagePost (1/3) public void unlike() {
if (likes > 0) {
likes--;
}
}

public void addComment(String text) {


comments.add(text);
}

public String getText() {


return message;
}

public long getTimeStamp() {


return timestamp;
}

10
Exemplo – Rede Social
public void display() {
System.out.println(username);
System.out.println(message);
System.out.print(timeString(timestamp));

▸ Métodos da classe if(likes > 0) {


System.out.println(" - " + likes + " people like this.");
MessagePost (2/3) }
else {
System.out.println();
}

if(comments.isEmpty()) {
System.out.println(" No comments.");
}
else {
System.out.println(" " + comments.size() +
" comment(s). Click here to view.");
}
}

11
Exemplo – Rede Social

▸ Métodos da classe private String timeString(long time) {


long current = System.currentTimeMillis();
MessagePost (3/3) long pastMillis = current - time;
long seconds = pastMillis/1000;
long minutes = seconds/60;
if(minutes > 0) {
return minutes + " minutes ago";
}
Tempo passado em else {
milissegundos return seconds + " seconds ago";
}
}

12
Exemplo – Rede Social
public class PhotoPost {
private String username; Nome do ficheiro com a imagem
private String filename;
private String caption;
private long timestamp;
Legenda da imagem
private int likes;
private ArrayList<String> comments;

public PhotoPost(String author, String filename, String caption)


{
username = author;
this.filename = filename;
this.caption = caption;
timestamp = System.currentTimeMillis();
likes = 0; ▸ Classe PhotoPost
comments = new ArrayList<String>();
}

// continua…

13
Exemplo – Rede Social
public void like() { ▸ Métodos da classe
likes++;
} PhotoPost (1/3)
public void unlike() {
if (likes > 0) {
likes--;
}
}
public void addComment(String text) {
comments.add(text);
}
public String getImageFile() {
return filename;
}
public String getCaption() {
return caption;
}

14
Exemplo – Rede Social
public long getTimeStamp() {
return timestamp;
▸ Métodos da classe
}
public void display() {
PhotoPost (2/3)
System.out.println(username);
System.out.println(" [" + filename + "]");
System.out.println(" " + caption);
System.out.print(timeString(timestamp));
if(likes > 0) {
System.out.println(" - " + likes + " people like this.");
}
else {
System.out.println();
}
if(comments.isEmpty()) {
System.out.println(" No comments.");
}
else {
System.out.println(" " + comments.size() + " comment(s). Click here to view.");
}
}

15
Exemplo – Rede Social

private String timeString(long time) { ▸ Métodos da classe


long current = System.currentTimeMillis();
long pastMillis = current - time; PhotoPost (3/3)
long seconds = pastMillis/1000;
long minutes = seconds/60;
if(minutes > 0) {
return minutes + " minutes ago";
}
else {
return seconds + " seconds ago";
}
}

16
Exemplo – Rede Social
public class NewsFeed {
private ArrayList<MessagePost> messages; ▸ Classe NewsFeed
private ArrayList<PhotoPost> photos;

public NewsFeed() {
messages = new ArrayList<MessagePost>();
photos = new ArrayList<PhotoPost>();
}

public void addMessagePost(MessagePost message) {


messages.add(message);
}

public void addPhotoPost(PhotoPost photo) {


photos.add(photo);
}

// continua…

17
Exemplo – Rede Social
// continuação da classe Newsfeed… ▸ Classe NewsFeed
public void show() {
// display all text posts
for(MessagePost message : messages) {
message.display();
System.out.println(); // empty line between posts
}

// display all photos


for(PhotoPost photo : photos) {
photo.display();
System.out.println(); // empty line between posts
}
}
}

18
Exemplo – Rede Social
▸ Problemas do protótipo criado:

▹ Duplicação de código

▹ As classes MessagePost e PhotoPost são bastante parecidas (grande parte do


código é idêntico).

▹ A manutenção do código dá mais trabalho.

▹ Corre-se o risco de se criarem bugs se não se alterar em todos os locais onde


o código está em duplicado.

▹ A classe NewsFeed também tem duplicação de código.


19
Herança de Classes
▸ Herança de Classes
Exemplo – Rede Social
▸ Classes MessagePost e PhotoPost
▹ Problema: Código duplicado

Atributos

Métodos

21
Exemplo – Rede Social
▸ Solução: Herança de classes Constroi-se uma
nova classe (Post)
com o código
comum.

Reescrevem-se as Representa a relação


classes anteriores de herança
para reutilizarem o
código comum
usando a herança

22
Herança de classes
Herança de classes
▸ É uma técnica usada em Programação Orientada por Objetos que vai permitir a reutilização de código.
▹ Define-se uma classe com o código comum.

▹ Criam-se outras classes com base na classe anterior que reutilizam esse código.

▹ A classe criada inicialmente é a superclasse e as classes que vão reutilizar essa classe são as subclasses.

▹ Ao processo de reutilização de uma classe dá-se o nome de Herança

▹ As subclasses vão herdar o código da superclasse

23
Exemplo – Rede Social
▸ Solução: usar a Herança de classes

Herança de classes (receita simples)

1. Define-se uma superclasse : Post


2. Define-se subclasses para MessagePost e PhotoPost
3. Na superclasse definem-se os atributos comuns
4. As subclasses herdam os atributos da superclasse
5. As subclasses adicionam outros atributos
24
Exemplo – Rede Social
▸ Herança de classes em Java
Sem alterações
public class Post {
...
}
Alterar aqui

public class PhotoPost extends Post {


...
}

public class MessagePost extends Post {


...
}

25
Exemplo – Rede Social

public class Post { ▸ Superclasse – Post


private String username;
private long timestamp;
private int likes;
private ArrayList<String> comments;

// construtores e métodos omitidos


}

26
Exemplo – Rede Social
public class MessagePost extends Post {
private String message; ▸ Subclasses –

// construtores e métodos omitidos MessagePost e


}
PhotoPost
public class PhotoPost extends Post {
private String filename;
private String caption;

// construtores e métodos omitidos


}

27
Exemplo – Rede Social
▸ Herança de classes em Java

Atributos
idênticos

28
Exemplo – Rede Social
▸ Herança de classes em Java

Métodos
idênticos

O BlueJ agrupa os métodos nas classes a


que pertencem no menu mas a utilização
em código não tem diferenças.
29
Exemplo – Rede Social
Herança de classes em Java

Mesma
utilização

30
Herança de classes
▸ Herança de classes

▹ A herança de classe define uma relação “is-a” entre classes.


▹ Ex: A classe MessagePost herda da classe Post:
Uma mensagem de texto (MessagePost) é uma (Is-a) mensagem (Post)

▹ Ex: A classe Carro herda da classe Veiculo:


Um carro é um (Is-a) veiculo

▹ Enquanto a composição define uma relação “has-a” entre classes


▹ Ex: A classe Carro contém (has-a) um objeto da classe Motor:
Um carro contém (has-a) motor
31
Hierarquias de classes
▸ A herança de classes leva à formação de hierarquias de classes

32
Herança – Hierarquia de Classes - vocabulário
Animal é superclasse
direta (classe base ou
classe pai) de Mammal e
Bird, e é super classe
Animal
indireta de Dog, Cat e Cow

Mammal é subclasse
direta (classe derivada ou
classe filha) de Animal e
super classe direta (base
Mammal Bird
ou pai) de Dog, Cat e Cow

Dog é subclasse direta


(classe derivada ou classe
filha) de Mammal e Dog Cat Cow
indirecta de Animal

33
Herança
▸ As subclasses ou classes derivadas vão herdar todos os atributos e métodos da superclasse ou classe
base.
▸ As subclasses não herdam os construtores da superclasse

34
Construtores em Herança

▸ Herança de Classes
Herança
▸ As subclasses não herdam os construtores da superclasse
public class Post {
private String username;
private long timestamp;
private int likes;
private ArrayList<String> comments;

public Post(String author) {


username = author;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}

public void like() {


likes++;
}
}

public class MessagePost extends Post {


private String message;
} 36
Herança
▸ As subclasses ou classes derivadas vão herdar todos os atributos e métodos da superclasse ou classe
base.
▸ As subclasses não herdam os construtores da superclasse
▸ Neste caso é necessário lidar com os construtores de uma forma diferente:
▹ Cada subclasse escolhe o construtor que vai usar da superclasse
▹ A escolha é feita no construtor da subclasse utilizando-se o método super() que representa uma chamada ao
construtor da super classe
▹ A escolha do construtor é feita dependendo dos argumentos do método super() Neste caso o número e tipo
dos argumentos deve corresponder ao número e tipo de argumentos do construtor que se pretende utilizar da
superclasse
▹ O método super() deve ser o primeiro método a ser chamado dentro do construtor da subclasse

37
Exemplo – Rede Social
public class Post {
private String username; ▸ Construtores em herança –
private long timestamp;
private int likes; classe Post
private ArrayList<String> comments;

public Post(String author) {


username = author;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}

// métodos omitidos
}

38
Exemplo – Rede Social
▸ Construtores em herança
public class MessagePost extends Post {
private String message; – classe MessagePost
public MessagePost(String author, String text) {
super(author);
message = text;
} Chamada ao
construtor da
// métodos omitidos superclasse (Post)
}

39
Construtores em herança
▸ Construtores em herança
▹ O construtor da subclasse deve incluir sempre uma chamada ao construtor da superclasse
▹ Se não for incluída o compilador coloca automaticamente uma chamada ao construtor sem
argumentos da superclasse: super();
▹ Apenas resulta se a superclasse tiver um construtor sem argumentos

▹ A chamada ao construtor da superclasse deve ser a primeira instrução do construtor da


subclasse.
public class MessagePost extends Post A classe Post é a superclasse da
{
private String message;
classe MessagePost

public MessagePost(String author, String text)


{
super(author);
message = text;
Chamada ao construtor da classe Post
}

// métodos omitidos
Primeira instrução do construtor
}
40
Herança
public class Post {
private String username;
▸ As subclasses não herdam
private long timestamp;
private int likes;
os construtores da
private ArrayList<String> comments;
superclasse
public Post(String author) {
username = author;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}
}

public class MessagePost extends Post { Novo construtor


private String message;

public MessagePost(String author) {


super(author);
message = "";
}

}
41
Herança por Extensão

▸ Herança de Classes
Modificadores de acesso do Java
▸ Membros public (símbolo + nos diagramas de classe): Se os membros são declarados como públicos
dentro de uma classe, então estes membros são acessíveis às classes que estão dentro e fora do
pacote onde esta classe é visível. Este é o menos restritivo de todos os modificadores de
acessibilidade.
▸ Membros protected (símbolo # nos diagramas de classe) : se os membros de uma classe são
declarados como protegidos, eles estarão acessíveis a todas as classes do pacote e a todas as
subclasses desta classe em qualquer pacote em que essa classe esteja visível.
▸ Membros default (nenhum símbolo) : quando nenhum modificador de acessibilidade é especificado para
o membro, ele é implicitamente declarado como visibilidade por defeito. Eles são acessíveis apenas
para as outras classes no pacote da classe.
▸ Membros private (símbolo - nos diagramas de classe) : Este é o mais restritivo de todos os
modificadores de acessibilidade. Esses membros são acessíveis apenas dentro desta classe. Eles não
são acessíveis a partir de nenhuma outra classe dentro do pacote da classe.
43
Exemplo – Rede Social
▸ Adição de outro tipo de Posts – EventPost

*métodos omitidos

Nova classe
EventPost.

44
Exemplo – Rede Social
▸ Hierarquias mais complexas
*métodos omitidos

Para classes que


têm em comum
comentários e
likes

45
Exemplo – Rede Social

Benefícios da Herança de classes


• Evita a duplicação de código

• Permite a reutilização de código

• Simplifica a manutenção

• Promove a extensão de classes

46
Principio da substituição

▸ Herança de Classes
Principio da Substituição
Subclasses e Subtipos
▸ Uma classe define um tipo
▸ Uma subclasse define um subtipo
▸ Sempre que é necessário um objeto de uma classe, pode-se usar em vez disso um objeto
de uma subclasse:
▹ Chama-se principio da substituição
▹ Exemplo
Post post = new MessagePost("João", "Olá Mundo");

Guarda um objeto da classe Post Atribui-se um objeto da subclasse MessagePost


48
Principio da Substituição
▸ Principio da substituição aplicado à atribuição de valores

Vehicle v1 = new Vehicle();


Vehicle v2 = new Car();
Vehicle v3 = new Bicycle();

49
Principio da Substituição
public class NewsFeed {
▸ Principio da substituição
public void addPost(Post post) aplicado à passagem de
{
... parâmetros
}
}

PhotoPost photo = new PhotoPost(...);


MessagePost message = new MessagePost(...);

feed.addPost(photo);
Objetos de subclasses
feed.addPost(message);
podem ser usados como
parâmetros atuais para
superclasses

50
Exemplo – Rede Social
public class NewsFeed {
private ArrayList<Post> posts;
▸ Nova classe NewsFeed
public NewsFeed() {
posts = new ArrayList<Post>();
}

public void addPost(Post post) {


posts.add(post);
}

public void show() {


for(Post post : posts) {
post.display();
System.out.println();
}
}
}

51
Exemplo – Rede Social
▸ Diagrama de objetos

52
Exemplo – Rede Social
▸ Diagrama de classes

53
Exemplo – Rede Social
▸ Antiga classe versus nova classe NewsFeed
public class NewsFeed { public class NewsFeed {
private ArrayList<MessagePost> messages; private ArrayList<Post> posts;
private ArrayList<PhotoPost> photos;
public NewsFeed() {
public NewsFeed() {
messages = new ArrayList<MessagePost>();
posts = new ArrayList<Post>();
photos = new ArrayList<PhotoPost>();
} }
public void addMessagePost(MessagePost message) {
messages.add(message); public void addPost(Post post) {
} posts.add(post);
public void addPhotoPost(PhotoPost photo) { }
photos.add(photo);
}
public void show() {
public void show() {
for(Post post : posts) {
for(MessagePost message : messages) {
message.display(); post.display();
System.out.println(); System.out.println();
} }
for(PhotoPost photo : photos) { }
photo.display(); }
System.out.println();
}
}
}
54
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes &


Michael Kölling, Pearson Education Limited, 2016
▹ Capítulo 10

55
Programação Orientada por Objetos

Herança de
classes -
exemplo
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Herança – Exemplo Xadrez

▸ Redefinição de métodos

▸ Herança – Exemplo Formas Geométricas

▸ Generalização versus especialização de classes

▸ A Classe Object
2
Exemplo – Xadrez
▸ Requisitos do protótipo:
▹ Representar os componentes do jogo sem
implementar as regras ou o desenrolar do jogo.
▹ Representar as peças: peão, torre, cavalo, rei,
rainha e bispo.
▹ Representar o tabuleiro de jogo com as posições.
▹ Deve ser possível obter em texto a posição de
cada peça usando a notação algébrica (ex: e5 –
peão na casa e5, ou Te7 – torre na casa e7).

3
Exemplo – Xadrez

Representação 1
▹ Cada peça poderá ser representada
por uma classe
▹ Peças: peão, torre, cavalo, rei, rainha
e bispo.
▹ O tabuleiro de jogo corresponde a
outra classe.

4
Exemplo – Xadrez

Representação 1
▹ Cada peça poderá ser
representada por uma classe
▹ Peças: peão, torre, cavalo, rei,
rainha e bispo.
▹ O tabuleiro de jogo
corresponde a outra classe.

5
Exemplo – Xadrez
▸ Representação 1 – Classes Pawn e Rook
public class Pawn { public class Rook {

private Colour colour; private Colour colour;


private Position position; private Position position;

public Pawn(Colour colour, Position position) { public Rook(Colour colour, Position position) {
this.colour = colour; this.colour = colour;
if (position != null) { if (position != null) {
this.position = position; this.position = position;
} else { } else {
this.position = new Position(); this.position = new Position();
} }
} }

public Colour getColour() { ... } public Colour getColour() { ... }


public Position getPosition() { ... } public Position getPosition() { ... }
public void setPosition(char x, int y) { ... } public void setPosition(char x, int y) { ... }
public void setPosition(Position position) public void setPosition(Position position)
{...} {...}
public void setY(int y) { ... } public void setY(int y) { ... }
public char getX() { ... } public char getX() { ... }
public int getY() { ... } public int getY() { ... }
public String toString() { ... } public String toString() { ... }
public String getName() { ... } public String getName() { ... }
} }
6
Exemplo – Xadrez private void setup() {
▸ Representação 1 – Classe ChessBoard for (char x = 'a'; x <= 'h'; x++) {
pawns.add(new Pawn(Colour.WHITE, new Position(x, 2)));
pawns.add(new Pawn(Colour.BLACK, new Position(x, 7)));
}
int line = 1;
public class Chessboard {
Colour colour = Colour.WHITE;
rooks.add(new Rook(colour, new Position('a', line)));
ArrayList<Pawn> pawns;
ArrayList<Knight> knights; knights.add(new Knight(colour, new Position('b', line)));
bishops.add(new Bishop(colour, new Position('c', line)));
ArrayList<Rook> rooks;
queens.add(new Queen(colour, new Position('d', line)));
ArrayList<Queen> queens;
kings.add(new King(colour, new Position('e', line)));
ArrayList<Bishop> bishops;
bishops.add(new Bishop(colour, new Position('f', line)));
ArrayList<King> kings;
knights.add(new Knight(colour, new Position('g', line)));
public Chessboard() { rooks.add(new Rook(colour, new Position('h', line)));
line = 8;
pawns = new ArrayList<>();
knights = new ArrayList<>(); colour = Colour.BLACK;
rooks = new ArrayList<>(); rooks.add(new Rook(colour, new Position('a', line)));
queens = new ArrayList<>(); knights.add(new Knight(colour, new Position('b', line)));
kings = new ArrayList<>(); bishops.add(new Bishop(colour, new Position('c', line)));
queens.add(new Queen(colour, new Position('d', line)));
bishops = new ArrayList<>();
kings.add(new King(colour, new Position('e', line)));
setup(); bishops.add(new Bishop(colour, new Position('f', line)));
} knights.add(new Knight(colour, new Position('g', line)));
rooks.add(new Rook(colour, new Position('h', line)));
}
}

7
Exemplo – Xadrez

▸ Representação 1 – Problemas da solução encontrada:

▹ Duplicação de código nas classes das peças.

▹ A representação do tabuleiro ficou complexa.

▹ Uma lista de peças por tipo de peça (6 listas no total)

8
Exemplo – Xadrez

Representação 2
▹ Criar uma classe peça que
representa qualquer peça
▹ Usar um atributo PieceType que
determina qual a peça
representada.
▹ O tabuleiro de jogo corresponde a
outra classe.

9
Exemplo – Xadrez
▸ Representação 2 – Classe Piece
public class Piece { public String toString() {
String text = "";
private Colour colour; switch(pieceType){
private Position position; case ROOK:
private PieceType pieceType; text += 'T';
break;
public Piece(PieceType pieceType, case KNIGHT:
Colour colour, Position position) { text += 'C';
this.pieceType = pieceType; break;
this.colour = colour;
case BISHOP:
if (position != null) {
text += 'B';
this.position = position;
} else { break;
this.position = new Position(); case QUEEN:
} text += 'D';
} break;
case KING:
public Colour getColour() { ... } text += 'R';
public PieceType getPieceType() { ... } break;
public void setPieceType(PieceType pieceType) { ... } }
public Position getPosition() { ... } text += position.toString();
public void setPosition(char x, int y) { ... } return text;
public void setPosition(Position position) { ... } }
public void setY(int y) { ... }
public char getX() { ... }
public int getY() { ... } public String getName() { ... }
}
}
10
Exemplo – Xadrez
▸ Representação 2 – private void setup() {

Classe ChessBoard
for (char x = 'a'; x <= 'h'; x++) {
pieces.add(new Piece(PieceType.PAWN, Colour.WHITE, new Position(x, 2)));
pieces.add(new Piece(PieceType.PAWN, Colour.BLACK, new Position(x, 7)));
}
int line = 1;
Colour colour = Colour.WHITE;
pieces.add(new Piece(PieceType.ROOK, colour, new Position('a', line)));
pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('b', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('c', line)));
pieces.add(new Piece(PieceType.QUEEN, colour, new Position('d', line)));
public class Chessboard { pieces.add(new Piece(PieceType.KING, colour, new Position('e', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('f', line)));
ArrayList<Piece> pieces; pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('g', line)));
pieces.add(new Piece(PieceType.ROOK, colour, new Position('h', line)));
public Chessboard() { line = 8;
pieces = new ArrayList<>(); colour = Colour.BLACK;
setup(); pieces.add(new Piece(PieceType.ROOK, colour, new Position('a', line)));
} pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('b', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('c', line)));
pieces.add(new Piece(PieceType.QUEEN, colour, new Position('d', line)));
pieces.add(new Piece(PieceType.KING, colour, new Position('e', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('f', line)));
pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('g', line)));
pieces.add(new Piece(PieceType.ROOK, colour, new Position('h', line)));
}
}
11
Exemplo – Xadrez

▸ Representação 2 – Problemas da solução encontrada:

▹ Classe Piece complexa. Tem problemas de coesão.

▹ Na classe Chessboard ter-se-á de utilizar vários switch sempre


que se quiser escolher entre os vários tipos de peça.

▹ Exemplo: na movimentação das peças.

12
Exemplo – Xadrez

▸ Solução:

Usar a herança de classes!

13
Exemplo – Xadrez
▸ Requisitos do protótipo:
▹ Representar os componentes do jogo sem
implementar as regras ou o desenrolar do jogo.
▹ Representar as peças: peão, torre, cavalo, rei,
rainha e bispo.
▹ Representar o tabuleiro de jogo com as
posições.
▹ Deve ser possível obter em texto a posição de
cada peça usando a notação algébrica (ex: e5 –
peão na casa e5, ou Te7 – torre na casa e7). 14
Exemplo – Xadrez (3)
▸ Representação 3 – Usar a herança:

▹ Definir uma classe Piece como superclasse.

▹ Inclui os atributos e métodos que são idênticos em todas as peças.

▹ Definir cada uma das peças como uma subclasse da classe Piece

▹ Na classe Chessboard ter uma única lista de peças tirando partido do


principio da substituição.

▹ Exemplo: na movimentação das peças.


15
Exemplo – Xadrez (3)
▸ Solução com
herança de
classes:

16
Exemplo – Xadrez (3)
▸ Exemplo de objetos da representação 1

Atributos
idênticos

Métodos
idênticos

17
Exemplo – Xadrez (3)
public class Piece {

▸ Classe Piece private Colour colour;


private Position position;

public Piece(Colour colour, Position position)


{
this.colour = colour;
if (position != null) {
this.position = position;
} else {
this.position = new Position();
}
}

// restante código omitido


}
18
Exemplo – Xadrez (3)
public Colour getColour() {
return colour;
▸ Classe Piece – métodos }

(1/2) public Position getPosition() {


return new Position(position.getX(), position.getY());
}

public void setPosition(char x, int y) {


position.setX(x);
position.setY(y);
}

public void setPosition(Position position) {


position.setX(position.getX());
position.setY(position.getY());
}

19
Exemplo – Xadrez (3)
public void setY(int y) {
position.setY(y);
}
▸ Classe Piece – métodos (2/2)
public char getX() {
return position.getX();
}

public int getY() {


return position.getY();
Podemos ter o método }
toString a retornar o
texto da posição @Override
public String toString() {
return position.toString();
}

20
Exemplo – Xadrez (3)

▸ Classe Rook

public class Rook extends Piece { Deriva da classe Piece

public Rook(Colour colour, Position position) {


super(colour, position);
}
Chamada ao construtor da classe
Piece (superclasse)
}

21
Classes Pawn e Rook

Exemplo – Xadrez (3)


Redefinição de
Métodos
▸ Herança de classes
Exemplo – Xadrez (3)
▸ Classe Rook
public class Rook extends Piece { Deriva da classe Piece

public Rook(Colour colour, Position position) {


super(colour, position);
}
Chamada ao construtor da classe Piece
(superclasse)
public String getName() {
return "Torre";
}
Este método é diferente em todas as classes das
peças
@Override
public String toString() {
return "T" + super.toString();
O método toString que é herdado
}
escreve apenas a posição da peça. É
} necessário reescrever este método
24
Exemplo – Xadrez (3)
public class Pawn extends Piece{
Deriva da classe Piece ▸ Classe Pawn
public Pawn(Colour colour, Position position) {
super(colour, position);
} Chamada ao construtor da classe
Piece (superclasse)

public String getName() {


return "Torre";
}
Este método é diferente em todas as classes
das peças
@Override
public String toString() {
return "P" + super.toString();
O método toString que é
}
herdado escreve apenas a
posição da peça. É necessário
} reescrever este método
25
Herança – Redefinição de métodos
▸ Por vezes os métodos herdados da superclasse não servem nas subclasses porque estão associados a
comportamentos próprios das subclasses.
▹ Ex: O método toString herdado da classe Piece devolve apenas a posição da peça na notação algébrica. Na classe
da Rook este método deve colocar a letra ‘T’ antes da posição
▸ Neste casos é necessário redefinir (override) esse método.
▹ A palavra @Override que aparece em cima do toString quer dizer que o método seguinte é a redefinição de um método que já
existe.
▸ No entanto é possível reutilizar os métodos da superclasse usando o prefixo super seguido de um ponto e do identificador
do método que se quer utilizar.

@Override Indica que se está a redefinir um método

public String toString() {


return "T" + super.toString();
}
Chamada ao método toString da superclasse
26
Exemplo – Xadrez (3)

▸ Classe Rook – Redefinição do método toString

27
Exemplo – Xadrez (3)
▸ Classes Queen, King, Bishop, Knight
▹ Omitidas: são semelhantes às anteriores public class Chessboard {
▸ Classe Chessboard
ArrayList<Piece> pieces;

public Chessboard() {
pieces = new ArrayList<>();
setup();
Apenas uma lista para }
guardar as várias peças
// métodos omitidos
}

28
private void setup() {

Exemplo –
for (char x = 'a'; x <= 'h'; x++) {
pieces.add(new Pawn(Colour.WHITE, new Position(x, 2)));
pieces.add(new Pawn(Colour.BLACK, new Position(x, 7)));
}
int line = 1;

Xadrez (3)
Colour colour = Colour.WHITE;
pieces.add(new Rook(colour, new Position('a', line)));
pieces.add(new Knight(colour, new Position('b', line)));
pieces.add(new Bishop(colour, new Position('c', line)));
pieces.add(new Queen(colour, new Position('d', line)));
pieces.add(new King(colour, new Position('e', line)));
pieces.add(new Bishop(colour, new Position('f', line)));
pieces.add(new Knight(colour, new Position('g', line)));
pieces.add(new Rook(colour, new Position('h', line)));
▸ Classe Chessboard –
line = 8;
método setup colour = Colour.BLACK;
pieces.add(new Rook(colour, new Position('a', line)));
pieces.add(new Knight(colour, new Position('b', line)));
pieces.add(new Bishop(colour, new Position('c', line)));
pieces.add(new Queen(colour, new Position('d', line)));
A inicialização tem pieces.add(new King(colour, new Position('e', line)));
de ser feita com o pieces.add(new Bishop(colour, new Position('f', line)));
pieces.add(new Knight(colour, new Position('g', line)));
mesmo detalhe pieces.add(new Rook(colour, new Position('h', line)));
}

29
Exemplo – Formas Geométricas

Requisitos do programa:

▸ Desenho de formas geométricas.

▸ Representar apenas círculos e quadrados.

▹ Deve ser possível saber as dimensões e a

posição de cada uma deles.

▹ Deve ser possível desloca-los.

30
Exemplo – Formas Geométricas

radius side

x,y

x,y

Circle Square

31
Exemplo – Formas Geométricas

32
Exemplo – Formas Geométricas
public class Circle { public int getX() {
private int x, y;
return x;
private int radius;
}
public Circle() {
public void setX(int x) {
this.x = 0;
this.x = x;
this.y = 0;
this. radius = 1; }
} public int getY() {
public Circle(int x, int y, int radius) { return y;
this.x = x;
}
this.y = y;
public void setY(int y) {
this. radius = radius;
this.y = y;
}
}
public int getRadius() {
return radius; public void move( int dx, int dy ) {

} x += dx; y += dy;
public void setRadius(int raio) { }
this. radius = radius; }
}

33
Exemplo – Formas Geométricas
public class Square { public int getX() {
private int x, y;
return x;
private int side;
}
public Square() {
this.x = 0; public void setX(int x) {
this.y = 0; this.x = x;
this.side = 1; }
}
public int getY() {
public Square(int x, int y, int side) {
return y;
this.x = x;
this.y = y; }
this.side = side; public void setY(int y) {
}
this.y = y;
public int getSide() {
}
return side;
} public void move( int dx, int dy ) {
public void setSide(int side) { x += dx; y += dy;
this.side = side; }
}
}
34
public class Program {
public static void main(String[] args) {
Circle circle = new Circle(1, 1, 23);

Exemplo – Square square = new Square(0, 0, 4);

System.out.println("Circulo: - Posição (" + circle.getX() +

Formas "," + circle.getY() +


") – Raio: " + circle.getRadius() );

Geométricas System.out.println("Quadrado: - Posição (" + square.getX() +


"," + square.getY() +
") – Lado: " + square.getSide() );
square.move( 2, 2);

System.out.println("Quadrado: - Posição (" + square.getX() +


"," + square.getY() +
") – Lado: " + square.getLado() );
}
}

35
Exemplo – Formas Geométricas
▸ As classes Circle e Square têm em comum alguns dos atributos e métodos

(código duplicado):

▹ 2 atributos (x e y)

▹ 5 getters & setters

▸ A solução é utilizar a herança criando uma superclasse GeometricShape e

definindo Circle e Square como Subclasses.

36
Exemplo – Formas Geométricas

37
Exemplo – Formas Geométricas
public int getX() {

public class GeometricShape { return x;


}
private int x, y;
public void setX(int x) {
this.x = x;
public GeometricShape () { }
x = 0; public int getY() {
y = 0; return y;
}
}
public void setY(int y) {
this.y = y;
public GeometricShape (int x, int y) { }
this.x = x; public void move( int dx, int dy ) {
this.y = y; x += dx; y += dy;
}
}
}
38
Exemplo – Formas public class Circle extends GeometricShape {

Geométricas private int radius;


public Circle() {
super(0, 0);
this.radius = 1;
}
public Circle(int x, int y, int radius) {
super(x, y);
this. radius = radius;
Acrescenta apenas o atributo
}
radius e os métodos seletores e
modificadores associados public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this. radius = radius;
}
}
39
Exemplo – Formas Geométricas
public class Square extends GeometricShape {
private int side;
public Square() {
super(0, 0); Acrescenta apenas o atributo side
this. side = 1; e os métodos seletores e
} modificadores associados
public Square(int x, int y, int side) {
super(x, y);
this. side = side;
}
public int getSide() {
return side;
}
public void setSize(int side) {
this. side = side;
}
} 40
Exemplo – Formas Geométricas
O método main do programa não sofre qualquer alteração

public class Program {


public static void main(String[] args) {
Circle circle = new Circle(1, 1, 23);
Square square = new Square(0, 0, 4);
System.out.println("Circulo: - Posição (" + circulo.getX() +
"," + circle.getY() +
") – Raio: " + circle.getRadius() );
System.out.println("Quadrado: - Posição (" + square.getX() +
E se quisermos criar "," + square.getY() +
uma nova classe para ") – Lado: " + square.getSide() );
representar um square.move( 2, 2);
System.out.println("Quadrado: - Posição (" + square.getX() +
segmento de reta? "," + square.getY() +
") – Lado: " + square.getSide() );
}
}

41
Exemplo – Formas Geométricas

Nova classe definida como


subclasse de
GeometricShape

42
Exemplo – Formas Geométricas
Acrescenta os atributos x2 e y2 para a
representação do segundo ponto do
segmento de retas
public class LineSegment extends GeometricShape {
public int getX2() {
private int x2, y2;
return x2;
public LineSegment() {
}
super(0, 0);
public void setX2(int x2) {
this.x2 = 1;
this.x2 = x2;
this.y2 = 1;
}
}
public int getY2() {
public LineSegment(int x, int y, int x2, int y2) {
return y2;
super(x, y);
}
this.x2 = x2;
Existe um método que public void setY2(int y2) {
this.y2 = y2; é herdado mas não é
this.y2 = y2;
} adequado nesta classe.
Qual? }
}
43
Herança – Redefinição de Métodos
▸ O método:
public void move( int dx, int dy ) {
x += dx; y += dy;
}
▹ Este método não funciona corretamente para objetos da classe LineSegment.
▹ A solução é redefinir este método nesta classe.
▹ Para redefinir um método basta defini-lo novamente no corpo da subclasse:

public class LineSegment extends GeometricShape {


// código omitido

@Override
public void move( int dx, int dy ) {
super.move(dx, dy);
x2 += dx; y2 += dy;
}
}
44
Exemplo – Formas Geométricas

45
Considerações Finais
▸ Herança de classes

46
Herança (is-a) – Generalização vs Especialização

▸ Uma superclasse de outras classes representa a generalização dessas classes


▹ A superclasse é mais genérica que as subclasses
▹ Por exemplo um Vehicle é uma generalização de Bike e Car.

Versus …

▸ Uma subclasse de uma dada classe é uma especialização dessa classe


▹ As subclasses especializam a sua superclasse
▹ A classe Dog é uma especialização da classe Animal.

47
Herança (is-a) – Generalização vs Especialização
▸ Na prática a herança utiliza-se por generalização e por especialização de classes

Generalização Up Top Especialização


Quando, numa aplicação, se Quando já existe classe genérica e se
verifica que várias classes verifica que a classe que se pretende
partilham um conjunto de criar pode ser derivada por herança
atributos e/ou métodos e que a dessa classe e onde essa herança faz
relação de herança pode ser sentido, dizemos que estamos a usar a
aplicada com a criação duma herança por especialização
classe mais genérica que faça
Down
sentido dizemos que estamos a
usar herança por generalização Bottom

48
Herança (is-a) – Generalização vs Especialização

o
lizaçã
a
ner
Ge

49
Herança (is-a) – Generalização vs Especialização

Es
pe
cia
liz

ão

50
Classe Object
▸ A classe Object é uma classe do Java, que está no topo da hierarquia e da
qual todas as outras são subclasses diretas ou indiretas.
▹ Todas as classes são uma especialização de Object, são todas um
tipo de objeto.
▹ Quando uma classe não deriva de outra o compilador de Java coloca-
a a derivar de Object (é acrescentado extends Object).
▹ A classe Object define um conjunto de métodos que são herdados
por todas as classes, entre eles estão os métodos: toString, equals
e hashCode utilizados antes.
▹ Por isso quando se coloca um destes métodos numa classe, na
prática está-se a redefinir o método herdado, sendo então
necessário colocar @Override antes da redefinição

51
Exemplo – Xadrez (3)
▸ Classe Object

52
Bibliografia

▸ Objects First with Java (6th


Edition), David Barnes & Michael
Kölling, Pearson Education Limited,
2016
▹ Capítulo 10

53
Programação Orientada por Objetos

Polimorfismo

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Principio da Substituição
▸ Tipos Estáticos e Tipos Dinâmicos

▸ Polimorfismo
▸ Exemplo Formas Geométricas

2
Principio da Substituição
▸ Polimorfismo

3
Exemplo – Rede Social

▸ Requisitos da rede social:


▹ Um pequeno protótipo com a base para o armazenamento e
apresentação de mensagens.

▹ Faz o armazenamento de mensagens de texto e mensagens de imagem.

▹ As mensagens de texto podem ter várias linhas;

▹ as mensagens de imagem têm uma imagem e uma descrição.

▹ Todas as mensagens devem incluir o seu autor, a altura em que foi


enviada, o número de “gostos” e a lista de comentários.

4
Exemplo – Rede Social
▸ Solução com herança de classes:
Classe base Post
com o código
comum

Classes derivadas
MessagePost e
PhotoPost

5
Exemplo – Rede Social
▸ Exemplo de utilização

O texto da
mensagem não é
mostrado

6
Exemplo – Rede Social
▸ Exemplo de utilização

O método display que


devia mostrar a
mensagem está na
classe Post

7
Exemplo – Rede Social
▸ Exemplo de utilização
public class Post {
private String username;
private long timestamp;
private int likes;
A informação mostrada está
private ArrayList<String> comments; incompleta para as classes
MessagePost e
// restante código omitido PhotoPost
public void display() {
System.out.println(username);
System.out.print(timeString(timestamp));
if(likes > 0) {
System.out.println(" - " + likes + " people like this.");
}
else {
System.out.println();
} Solução:
if(comments.isEmpty()) { Redefinir o método nas
System.out.println(" No comments."); classes derivadas
}
else {
System.out.println(" " + comments.size() +
" comment(s). Click here to view.");
}
}
} 8
Exemplo – Rede Social
public class MessagePost extends Post {
private String message;

▸ Exemplo de utilização
// restantes métodos omitidos

@Override
public void display() {
super.display();
System.out.println(message);
}
}

public class PhotoPost extends Post {


private String filename;
private String caption;
// restantes métodos omitidos
Reutiliza o método
@Override
display herdado public void display() {
super.display();
System.out.println(" [" + filename + "]");
System.out.println(" " + caption);
}
}
9
Exemplo – Rede Social
▸ Exemplo de utilização

Acrescenta o texto da
mensagem no caso de
objetos MessagePost

10
Principio da Substituição
▸ Subclasses e Subtipos
▸ Uma classe define um tipo
▸ Uma subclasse define um subtipo
▸ Sempre que é necessário um objeto de uma classe pode-se usar em vez disso um
objeto duma subclasse:
▹ Chama-se principio da substituição
▹ Exemplo
Post post = new MessagePost("João", "Olá Mundo");

Atribui-se um objeto da subclasse


Guarda um objeto da classe Post
MessagePost
11
Principio da Substituição
▸ Exemplo de utilização

display de
MessagePost

display de PhotoPost

12
Principio da Substituição
▸ Exemplo de utilização

A variável post é do tipo Post mas


pode guardar objetos de tipos
derivados como é o caso com objetos
dos tipos MessagePost e
PhotoPost

13
Principio da Substituição
▸ Exemplo de utilização

▸ O método display que é executado é o que está no objeto guardado


na variável
▹ display do objeto MessagePost mostra o texto da mensagem
▹ display do objeto PhotoPost mostra o ficheiro e a legenda da
imagem

14
Tipos Estáticos e
Dinâmicos
▸ Polimorfismo

15
Exemplo – Rede Social

▸ Novo requisito da rede social adicional:


▹ Os posts de imagem e de texto devem devolver o texto
associado à mensagem.
▹ No caso das mensagens de texto corresponde ao
texto da mensagem.
▹ No caso das mensagens de imagem corresponde à
legenda da imagem.
▹ Cada tipo de mensagem deve definir o texto que
retorna.

16
Exemplo – Rede Social
▸ Classe MessagePost

public class MessagePost extends Post {


private String message;

public MessagePost(String author, String text) {


super(author);
message = text;
}

// métodos omitidos
O método requerido
public String getText() { já existia na classe
return message; MessagePost
}

}
17
Exemplo – Rede Social
▸ Classe PhotoPost

public class PhotoPost extends Post {


private String filename;
private String caption;

public PhotoPost(String author, String filename, String caption) {


super(author);
this.filename = filename;
this.caption = caption;
}
// métodos omitidos
Novo método
public String getText() {
return caption;
}

}
18
Exemplo – Rede Social
▸ Superclasse – Post

public class Post {


private String username;
private long timestamp;
private int likes;
private ArrayList<String> comments;

// construtores e métodos omitidos


}

19
Principio da Substituição
▸ Exemplo de utilização – método getText

Erro???

Não funciona porque o


método getText não
existe na classe base
Post

20
Tipos Estáticos e Tipos dinâmicos

MessagePost p1 = new MessagePost("José", "Olá pessoal"); Qual é o tipo de p1?

Post p2 = new MessagePost ("José", "Olá pessoal"); Qual é o tipo de p2?

21
Tipos Estáticos e Tipos dinâmicos
▸ O tipo declarado de uma variável é o seu tipo estático (static type)
▸ O tipo do objeto referido pela variável é o seu tipo dinâmico (dynamic type)
▸ O compilador verifica sempre se existem erros nos tipos estáticos.
▹ Não é possível referir objetos que não sejam da classe do tipo estático ou de uma das classes
derivadas do mesmo.
▹ Não é possível chamar métodos para uma variável que não existam no seu tipo estático. Ou seja, que
não existam na classe declarada.

Post p2 = new MessagePost ("José", "Olá pessoal");

22
Tipos Estáticos e Tipos dinâmicos

▸ A variável post é do tipo (estático) Post


▸ O método getText não existe na classe Post (o seu tipo estático)
▸ O compilador deteta que o método getText não existe para a classe Post e dá erro de compilação
▹ No caso mostrado o BlueJ acusa o erro quando está a interpretar o código

23
Tipos Estáticos e Tipos dinâmicos
No caso do método
display não há
problema porque existe
na classe base e nas
classes derivadas

Satisfaz a verificação
estática (e dinâmica)
do tipo

24
Tipos Estáticos e Tipos dinâmicos

O método getText
só existe nas
classes derivadas

Não satisfaz a
verificação estática
do tipo

25
Tipos Estáticos e Tipos dinâmicos

▸ Em tempo de execução (runTime)

▸ O método display que é executado é o que está no objeto guardado


na variável (o seu tipo dinâmico)
▹ display do objeto MessagePost mostra o texto da mensagem
▹ display do objeto PhotoPost mostra o ficheiro e a legenda da
imagem

26
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime)

▸ O método display que é executado é o que está no objeto guardado


na variável (o seu tipo dinâmico)
▸ Durante a execução é procurado o método a executar (Method lookup)
de acordo com o objeto que está guardado na variável
▹ … e não com o tipo da variável

27
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

Tipo estático e tipo


dinâmico são iguais:
PhotoPost

O método display a
executar apenas pode
ser o que está em Neste exemplo não
PhotoPost existe herança
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

Tipo estático e tipo


dinâmico são iguais:
PhotoPost

O método display é procurado na


classe do objeto PhotoPost mas
como não é encontrado é procurado Neste exemplo existe
na classe pai Post. Ao ser herança mas não existe
encontrado é executado esse método redefinição do método
29
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

O tipo estático é Post e o


tipo dinâmico é
PhotoPost

O método display é procurado na


Neste exemplo existe
classe do objeto PhotoPost e como
é encontrado é executado herança e existe
redefinição do método
Tipos Estáticos e Tipos dinâmicos
▸ Sumário da procura do método a executar (Method lookup) em tempo de execução (Run time):
1. A variável é acedida
2. É obtido o nome do método a executar
3. É obtida a classe do objeto referido
4. O método a executar é procurado na classe do objeto
5. Se não for encontrado o método, é procurado na sua superclasse
6. O passo anterior é repetido até que o método seja encontrado
7. Os métodos redefinidos aparecem primeiro tendo assim precedência sobre os outros métodos

31
Polimorfismo
▸ Polimorfismo

32
Polimorfismo
▸ Quando executamos um método, o método que é executado depende da classe do objeto que o
executa. Neste caso dizemos que temos polimorfismo de métodos.
▹ Um método várias (poli) formas (Morfo) de resposta
▹ O método display tem várias formas de resposta, dentro da classe MessagePost tem uma
forma, dentro da classe PhotoPost tem outra forma.

▸ Uma variável polimórfica pode guardar objetos de vários tipos

▸ A chamada de métodos em Java é polimórfica.

▹ O método que realmente é chamado depende do tipo do objeto em tempo de execução (o tipo dinâmico)
33
Polimorfismo

▸ Para solucionar este erro ter-se-ia que criar o método getText na classe
Post mesmo que não fizesse nada
public class Post {

// restante código omitido

public String getText(){


return "";
}
}
34
Polimorfismo

▸ Na execução, o método getText executado vai corresponder ao método getText da classe do objeto
public class PhotoPost extends Post {
public class MessagePost extends Post { private String filename;
private String message; private String caption;
// código omitido // código omitido
@Override @Override
public String getText() { public String getText() {
return message; return caption;
} }
} }
35
Polimorfismo
▸ Poliformismo

36
▸ Polimorfismo

Exemplo Formas
geométricas

37
Exemplo – Formas Geométricas
▸ Requisitos do programa:
▹ Desenho de formas geométricas.
▹ Representar círculos, quadrados e retângulos.
▹ Deve ser possível saber as dimensões e a posição de cada um deles.
▹ Deve ser possível deslocá-los.

38
Polimorfismo - exemplo
▸ Exemplo de formas geométricas

raio radiu x,
s y
x,y
x,y
x,y
lad
x2,y o
2
Círculo Retângulo Quadrado

39
Polimorfismo - exemplo

40
Polimorfismo - exemplo public class circle extends Shape {
private int radius;

public class Shape { public circle() { this.radius = 1; }

private int x, y; public circle(int x, int y, int radius) {


super(x, y); this.radius = radius; }
public Shape () {
public int getRadius() { return radius; }
x = 0;
public void setRadius(int radius) { this.radius = radius; }
y = 0;
}
}
public Shape (int x, int y) {
public class Rectangle extends Shape {
this.x = x; private int x2, y2;
this.y = y; public Rectangle() {this.x2 = 0; this.y2 = 0; }
public Rectangle(int x, int y, int x2, int y2) {
} super(x, y); this.x2 = x2;this.y2 = y2; }
public int getX2() { return x2; }
public int getX() {
public void setX2(int x2) { this.x2 = x2; }
return x; public int getY2() { return y2; }
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
this.x = x; public class Square extends Rectangle {
private int side;
}
public Square() {this.side = 1;}
public int getY() {
public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
} public int getSide() {return side;}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side); setY2(getY() - side); }
this.y = y;
@Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
}
41
Polimorfismo - exemplo
▸ Requisitos
▹ Pretende-se criar um desenho com base em formas geométricas.
▹ É necessário calcular a área ocupada pelas formas geométricas

42
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape { public circle() { this.radius = 1; }
private int x, y; public circle(int x, int y, int radius) {
super(x, y); this.radius = radius; }
public Shape () { public int getRadius() { return radius; }
x = 0; public void setRadius(int radius) { this.radius = radius; }
y = 0; }
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; public Rectangle() {this.x2 = 0; this.y2 = 0; }
this.y = y; public Rectangle(int x, int y, int x2, int y2) {

} Podemos adicionar super(x, y); this.x2 = x2;this.y2 = y2; }


public int getX2() { return x2; }
public int getX() { um método getArea public void setX2(int x2) { this.x2 = x2; }
return x; em cada uma das public int getY2() { return y2; }

formas geométricas!!!
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 43
Polimorfismo - exemplo
public class Shape {
public class circle extends Shape {
private int radius;

@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
getArea do
}
círculo
x = 0;
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; public Rectangle() {this.x2 = 0; this.y2 = 0; }
this.y = y; public Rectangle(int x, int y, int x2, int y2) {
super(x, y); this.x2 = x2;this.y2 = y2; }
}
public int getX2() { return x2; }
public int getX() { public void setX2(int x2) { this.x2 = x2; }
return x; public int getY2() { return y2; }
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 44
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape {
@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
x = 0; }
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; @Override
this.y = y; public double getArea() {

} double dx = x2 - getX();

public int getX() { getArea do double dy = y2 - getY();


return Math.abs(dx * dy);
return x; Retângulo }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 45
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape {
@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
x = 0; }
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; @Override
this.y = y; public double getArea() {

} double dx = x2 - getX();
double dy = y2 - getY();
public int getX() {
return Math.abs(dx * dy);
return x; }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
getArea do quadrado private int side;

não é necessária, o
}
public int getY() {
return y;
método herdado serve
} // Restante código omitido
}
public void setY(int y) {
this.y = y;
}
} 46
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
@Override
public class Shape {
public double getArea() {
private int x, y;
return Math.PI*radius*radius;

}
public Shape () {
x = 0; } // Restante código omitido

y = 0;
public class Rectangle extends Shape {
}
private int x2, y2;
public Shape (int x, int y) {

this.x = x; Teremos de adicionar o @Override


public double getArea() {
this.y = y; método getArea à classe double dx = x2 - getX();
} Shape para o double dy = y2 - getY();
public int getX() {
polimorfismo funcionar return Math.abs(dx * dy);
return x; }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
}
public int getY() {

return y;
} } // Restante código omitido

public void setY(int y) {

this.y = y;
}

} 47
public class circle extends Shape {
private int radius;

Polimorfismo - exemplo @Override

public double getArea() {


return Math.PI*radius*radius;
public class Shape { }

private int x, y; } // Restante código omitido

public class Rectangle extends Shape {


public Shape () { private int x2, y2;
x = 0; @Override
public double getArea() {
y = 0;
double dx = x2 - getX();
} double dy = y2 - getY();
public Shape (int x, int y) { return Math.abs(dx * dy);

this.x = x; O método getArea não faz }


} // Restante código omitido
this.y = y; sentido nesta classe mas
} tem de ser criado para o public class Square extends Rectangle {

polimorfismo funcionar.
private int side;

Colocamo-lo a retornar 0.0!


// Restante código omitido
public double getArea() { } // Restante código omitido

return 0.0;
}
} 48
Polimorfismo - exemplo
Usamos o principio da
public class Main{ substituição para guardar
public static void main(String[] args) { as diferentes formas
Shape[] shapes; geométricas no mesmo
shapes = new Shape[6]; array
shapes[0] = new circle(4, 8, 3);
shapes[1] = new circle(28, 10, 4);
shapes[2] = new Rectangle(3, 0, 5, 6);
shapes[3] = new Rectangle(11, 0, 23, 6);
shapes[4] = new Rectangle(18, 0, 21, 4); Calculamos a área
shapes[5] = new Square(13, 2, 2); aplicando o
double totalArea = 0; polimorfismo. Cada
forma geométrica
for (int i=0; i < shapes.length; i++) { retorna a sua área
totalArea += shapes[i].getArea();
}
System.out.println("Area Total: " + totalArea);
}
}
49
Polimorfismo - exemplo
public class Main{
public static void main(String[] args) {
ArrayList<Shape> shapes = new ArrayList<>();
shapes.add(new circle(4, 8, 3));
shapes.add(new circle(28, 10, 4));
shapes.add(new Rectangle(3, 0, 5, 6));
Alternativa
shapes.add(new Rectangle(11, 0, 23, 6)); com listas!
shapes.add(new Rectangle(18, 0, 21, 4));
shapes.add(new Square(13, 2, 2));
double totalArea = 0;

for (Shape shape : shapes) {


totalArea += shape.getArea();
}
System.out.println("Area Total: " + totalArea);
}
}

50
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes &


Michael Kölling, Pearson Education Limited, 2016
▹ Capítulo 11

51
Programação Orientada por Objetos

Polimorfismo
- Exemplo
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Exemplo Desenho Casa – Solução 1

▸ Exemplo Desenho Casa – Solução 2

▸ Exemplo Desenho Casa – Herança e Polimorfismo

▸ Organização de Código

2
Desenho Casa –
Solução 1
▸ Polimorfismo

3
Exemplo – Desenho Casa
▸ Requisitos do desenho (Revisitado):
▹ O desenho é composto por quadrados, triângulos, círculos e
retângulos.
▹ Cada uma das formas geométricas tem uma cor e uma
posição.
▹ O desenho reutiliza as classes Pen e Canvas do ambiente
gráfico. Neste caso as figuras são desenhadas apenas com as
linhas de contorno.
▹ Deverá ser possível criar facilmente outros desenhos com as
figuras geométricas existentes (circulo, quadrado, triângulo e
retângulo).
4
Exemplo – Desenho Casa
Foram criadas 2 soluções:

▸ Solução 1:

▹ Classes para cada tipo de forma geométrica: Square, Rectangle,

Circle, Triangle.

▹ Uma classe Figure que pode ser qualquer das figuras existentes.

▹ Uma classe Drawing para o desenho que guarda uma lista de figuras.

5
Exemplo – Desenho casa
▸ Classe Position para representar a posição das figuras:
public class Position { public int getX() {
return x;
private int x; }
private int y;
public int getY() {
public Position() { return y;
x = 0; }
y = 0;
} public void setX(int x) {
this.x = x;
public Position(int x, int y) { }
this.x = x;
this.y = y; public void setY(int y) {
} this.y = y;
}
// continua ao lado... }
6
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Square
public class Square { É necessária uma pen
externa para desenhar o
private Pen pen; quadrado na superfície
private Color color; associada
private Position position;
private int side;

public Square(Pen pen, Color color,Position position, int side) {


this.pen = pen;
this.color = color;
this.position = position;
this.side = side;
}
// restante código
}
7
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Square public class Square {

// restante código
public void draw() {
pen.setColor(color);
pen.penUp();
pen.moveTo(position.getX(),position.getY());
pen.penDown();
Através dos pen.turnTo(0);
movimentos da pen
é possível efetuar o for(int i=0; i<4; i++) {
desenho duma figura pen.move(side);
pen.turn(90);
}
}
}
8
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▸ Classe Circle public class Circle {
private Pen pen;
private Color color;
private Position position;
private int radius;
Vários atributos
em comum com
a classe Square public Circle(Pen pen,Color color,Position position, int radius) {
this.pen = pen;
this.color = color;
this.position = position;
this.radius = radius;
}
// restante código
}
9
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Circle
public class Circle {
// restante código
public void draw() {
pen.setColor(color);
pen.penUp();
pen.moveTo(position.getX(),position.getY());
Estamos a
pen.penDown();
criar círculos pen.turnTo(0);
aproximados
int sides = 50;
por polígonos
int side = (int)(2*Math.PI*radius/sides);
regulares de
for(int i=0; i<sides+1; i++) {
50 lados.
pen.move(side);
pen.turn((int)(360.0/sides));
}
}
}
10
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Triangle public class Triangle {
private Pen pen;
private Color color;
private Position position
private int height, width;
Com atributos
// restante código
em comum com
void draw() {
as outras classes pen.penUp();
pen.moveTo(x, y);
pen.penDown();
pen.turnTo(0);

pen.moveTo(x + (width / 2), y - height);


pen.moveTo(x + width, y);
pen.moveTo(x, y);
}
}
11
Exemplo – Desenho casa
public class Rectangle {
▸ Solução 1 – Implementar classes para as figuras private Pen pen;
geométricas private Color color;
private Position position
▹ Classe Rectangle private int height, width;
void draw() {
pen.penUp();
pen.moveTo(x,y);
pen.penDown();
Com atributos pen.turnTo(0);
em comum com pen.move(width);
pen.turn(90);
as outras classes
pen.move(height);
pen.turn(90);
pen.move(width);
pen.turn(90);
pen.move(height);
pen.turn(90);
} // restante código
} 12
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Classe Figure

public class Figure { Tipo enumerado FigureType para


definir o tipo de figura guardado

private FigureType figureType;


private Square square;
Atributos
private Retangulo rectangle;
necessários para
private Triangulo triangle; os vários tipos de
private circle circle; figura que poderão
ser utilizados
// restante código
}

13
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Tipo FigureType

public enum FigureType {

TRIANGLE, CIRCLE, RECTANGLE, SQUARE


}

14
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Classe Figure - Construtores
public Figure(FigureType figureType, Pen pen, Color color,
Position position, int dimension) {
this.figureType = figureType;
if (figureType == FigureType.CIRCLE) {
circle = new circle(pen, color, position, dimension);
} else { Confuso!
square = new Square(pen, color, position, dimension);
}
}
public Figure(FigureType figureType, Pen pen, Color color,
Position position, int height, int width) {
this.figureType = figureType;
if (figureType == FigureType.TRIANGLE) {
triangle = new Triangulo(pen, color, position, height, width);
} else {
rectangle = new Retangulo(pen, color, position, height, width);
}
}
15
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Classe Figure - draw public void draw() {
switch(figureType) {
case CIRCLE:
Um switch para lidar circle.draw();
com os diferentes tipos break;
case TRIANGLE:
de figura
triangle.draw();
break;
case RECTANGLE:
rectangle.draw();
break;
Muitas vezes as instruções switch são pistas que
nos indicam que poderá existir um problema de case SQUARE:
coesão onde estão representadas várias square.draw();
entidades e em que a solução passa pela break;
herança (e polimorfismo)
}
}
16
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
public class Drawing {
▹ Classe Drawing private ArrayList<Figure> figures;

public Drawing() {
figures = new ArrayList<>();
}

public void addFigure(Figure figure) {


if(figure != null) {
figures.add(figure);
}
}

public void draw() {


for(Figure figure : figures ) {
figure.draw();
}
}
} 17
Exemplo – Desenho casa
▸ Solução 1 – Programa principal
▹ Ex: representar o desenho da casa
public class Program {

public static void main() {


Canvas canvas = new Canvas("House", 500, 300, Color.WHITE);
Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Figure wall = new Figure(FigureType.SQUARE, pen, Color.RED,


new Position(170, 140), 120);
Figure window = new Figure(FigureType.SQUARE, pen, Color.BLACK,
new Position(190, 160), 40);
Figure roof = new Figure(FigureType.TRIANGLE, pen, Color.GREEN,
new Position(140, 138), 80, 180);
Figure sun = new Figure(FigureType.CIRCLE, pen, Color.YELLOW,
new Position(360, 40), 40);
// continua
18
Exemplo – Desenho casa
▸ Solução 1 – Programa principal
▹ Ex: representar o desenho da casa

drawing.addFigure(wall);
drawing.addFigure(window);
drawing.addFigure(roof);
drawing.addFigure(sun);

drawing.draw();
}
}

19
Exemplo – Desenho casa

▹ Solução 1 – Diagrama de classes

20
Desenho Casa –
Solução 2
▸ Polimorfismo

21
Exemplo – Desenho Casa
▸ Requisitos do desenho (Revisitado):
▹ O desenho é composto por quadrados, triângulos, círculos e
retângulos.
▹ Cada uma das formas geométricas tem uma cor e uma
posição.
▹ O desenho reutiliza as classes Pen e Canvas do ambiente
gráfico. Neste caso as figuras são desenhadas apenas com
as linhas de contorno.
▹ Deverá ser possível criar facilmente outros desenhos com as
figuras geométricas existentes (circulo, quadrado, triângulo
e retângulo).
22
Exemplo – Desenho Casa
▸ Foram criadas 2 soluções:
▹ Solução 1:
▹ Classes para cada tipo de forma geométrica: Square, Rectangle,
Circle, Triangle.
▹ Um classe Figure que pode ser qualquer das figuras existentes.
▹ Uma classe Drawing para o desenho que guarda uma lista de figuras.
▹ Solução 2:
▹ Uma classe Figure que representa qualquer figura.
▹ Uma classe Drawing para o desenho que guarda uma lista de figuras.

23
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure
public class Figure { Tipo enumerado TipoFigura para
definir o tipo de figura guardado
private FigureType type;
private Pen pen;
private Position position;
private Color color;
private int height; Atributos
private int width;
necessários para
private int radius;
os vários tipos de
figura que poderão
// restante código
}
ser utilizados

24
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Tipo FigureType

public enum FigureType {

NONE, TRIANGLE, CIRCLE, RECTANGLE, SQUARE


}

25
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - Construtor

public Figure(Pen pen) {


this.pen = pen;
type = FigureType.NONE;
position = new Position();
}

26
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - setCircle e setSquare

public void setCircle(Position position, int radius) {


type = FigureType.CIRCLE;
this.position = position;
this.radius = radius;
}
public void setSquare(Position position, int side) {
type = FigureType.SQUARE;
this.position = position;
this.width = side;
this.height = side;
}
27
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - setRectangle e setTriangle

public void setRectangle(Position position, int width, int height) {


type = FigureType.RECTANGLE;
this.position = position;
this.width = width;
this.height = height;
}
public void setTriangle(Position position, int base, int height) {
type = FigureType.TRIANGLE;
this.position = position;
this.width = base;
this.height = height;
}
28
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - draw
public void draw() {
if (position == null || type == FigureType.NONE || pen == null) {
return;
}

pen.penUp();
pen.moveTo(position.getX(), position.getY());
pen.setColor(color);
pen.penDown();

// continua…

29
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - draw
switch (type) {
case CIRCLE:
drawCircle();
break;
case TRIANGLE:
drawTriangle();
break;
case RECTANGLE:
drawRectangle();
break;
case SQUARE:
drawSquare();
break;
}
}
30
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - drawSquare e drawCircle
private void drawSquare() {
for (int i = 0; i < 4; i++) {
pen.move(width);
pen.turn(90);
}
}
private void drawCircle() {
int sides = 50;
int side = (int) (2 * Math.PI * radius / sides);

for (int i = 0; i < sides + 1; i++) {


pen.move(side);
pen.turn((int) (360.0 / sides));
}
}
31
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - drawRectangle

private void drawRectangle() {


pen.move(width);
pen.turn(90);
pen.move(height);
pen.turn(90);
pen.move(width);
pen.turn(90);
pen.move(height);
pen.turn(90);
}

32
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - drawTriangle

private void drawTriangle() {


int x = position.getX();
int y = position.getY();

pen.moveTo(x + (width / 2), y - height);


pen.moveTo(x + width, y);
pen.moveTo(x, y);
}

33
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Drawing public class Drawing {
private ArrayList<Figure> figures;

public Drawing() {
figures = new ArrayList<>();
}

public void addFigure(Figure figure) {


if (figure != null) {
figures.add(figure);
}
}
Idêntica à solução 1
public void draw() {
for (Figure figure : figures) {
figure.draw();
}
}
}
34
Exemplo – Desenho casa
▸ Solução 2 – Programa public class Program {
principal
▹ Ex: representar o
public static void main() {
Canvas canvas = new Canvas("House", 500, 300, Color.WHITE);
desenho da casa Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Figure wall = new Figure(pen);


wall.setCor(Color.RED);
wall.setSquare(new Position(170, 140), 120);
drawing.addFigure(wall);

Figure window = new Figure(pen);


window.setCor(Color.BLACK);
window.setSquare(new Position(190, 160), 40);
drawing.addFigure(window);
// continua

35
Exemplo – Desenho casa
▸ Solução 2 – Programa principal
▹ Ex: representar o desenho da casa

Figure roof = new Figure(pen);


roof.setCor(Color.GREEN);
roof.setTriangle(new Position(140, 138), 180, 60);
drawing.addFigure(roof);

Figure sun = new Figure(pen);


sun.setCor(Color.YELLOW);
sun.setCircle(new Position(360, 50), 40);
drawing.addFigure(sun);

drawing.draw();
}

36
Exemplo – Desenho casa

▹ Solução 2 – Diagrama de classes

37
Desenho Casa:
Herança e Polimorfismo
▸ Polimorfismo

38
Exemplo – Desenho Casa
▸ Problemas das soluções apresentadas:
▹ Solução 1
▹ Código duplicado nas várias formas geométricas
▹ Classe Figure com um atributo por cada tipo de forma geométrica
▹ Solução 2
▹ Classe Figure complexa
▹ Classe Figure com problemas de coesão ao representar as várias
formas geométricas

39
Exemplo – Desenho Casa
▸ Solução com herança de classes e polimorfismo:
▹ Criar uma superclasse Figure com os atributos comuns das formas
geométricas
▹ Criar sublasses para cada uma das formas geométricas.
▹ Criar uma classe Drawing que irá guardar uma lista de formas
geométricas tirando partido do Principio da Substituição
▹ Método draw da classe Figure polimórfico.
▹ O resultado da sua execução depende da figura que estiver na lista.

40
Exemplo – Desenho Casa
▸ Classes da solução 1: public class Rectangle {
public class Square {
private Pen pen;
private Pen pen; private Color color;
private Color color; private Position position;
private Position position; private int height, width;
private int side;
// restante código
// restante código Vários atributos
em comum }
}

public class Triangle {


public class Circle {
private Pen pen;
private Pen pen; private Color color;
private Color color; Apenas pen, color e private Position position;
private Position position; position fazem
private int height, width;
private int radius; sentido na classe
Figure
// restante código
// restante código }
}
41
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure
public class Figure {

private Pen pen;


private Position position;
private Color color;

Construtor sem
public Figure() {
argumentos
pen = new Pen();
position = new Position();
color = Color.BLACK;
}

// restante código
}
42
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores
public Figure(Position position,
public Figure() {
Pen pen, Color color) {
pen = new Pen();
if (pen != null) {
position = new Position();
this.pen = pen;
color = Color.BLACK;
} else {
}
this.pen = new Pen();
}
public Figure(Pen pen, Color color) {
if (position != null) {
if (pen != null) {
this.position =
this.pen = pen;
new Position(position.getX(),
} else {
position.getY());
this.pen = new Pen();
} else {
}
this.position = new Position();
position = new Position();
}
if (color != null) {
if (color != null) {
this.color = color;
this.color = color;
} else {
this.color = Color.BLACK; Aqui também existe } else {
this.color = Color.BLACK;
} alguma duplicação
}
} de código }

43
Construtores – this()
public class Clock {
▸ this() nos construtores
private int hours;
▹ Da mesma maneira que se utiliza private int minutes;
private int seconds;
super() para a chamada ao Chamada ao construtor:
public Clock(){ Clock(int hours, int
construtor da classe base é this(0,0,0); minutes, int seconds)
}
possível usar this() para a
public Clock(int hours, int minutes){
chamada doutro construtor da this(hours, minutes, 0);
própria classe. }

▹ Neste caso mantém-se public Clock(int


this.hours =
hours, int minutes, int seconds){
hours;
a obrigatoriedade da this.minutes
this.seconds
= minutes;
= seconds;
instrução this() ser a }

primeira do construtor // […]


}

44
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores
public Figure(Position position,
Pen pen, Color color) {
if (pen != null) {
this.pen = pen;
public Figure() { } else {
this(new Position(), new Pen(), Color.BLACK); this.pen = new Pen();
} }
if (position != null) {
this.position =
public Figure(Pen pen, Color color) {
new Position(position.getX(),
this(new Position(), pen, color); position.getY());
} } else {
this. position = new Position();
}
if (color != null) {
this.color = color;
Assim evita-se a } else {
duplicação de código nos
this.color = Color.BLACK;
construtores }
} 45
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores
public Figure(Position position,
Pen pen, Color color) {
public Figure() { if (pen != null) {
this(new Position(), new Pen(), Color.BLACK); this.pen = pen;
} } else {
this.pen = new Pen();
}
public Figure(Pen pen, Color color) {
if (position != null) {
this(new Position(), pen, color); this.position =
} new Position(position.getX(),
position.getY());
} else {
this. position = new Position();
}
Outra simplificação que
if (color != null) {
pode ser feita é a utilização this.color = color;
do operador ternário em } else {
substituição dos if this.color = Color.BLACK;
}
} 46
Operador ternário
▸ Operador ternário – ?!

valor se a condição for valor se a condição for


Condição lógica falsa
verdadeira

condição ? valor 1 : valor 2

Ø Exemplo: String period = (hours < 12) ? "am" : "pm";

String period;
if (hours < 12) {
Ø Equivalente com ifs: period = "am";
} else {
period = "pm";
}
47
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores

public Figure() {
this(new Position(), new Pen(), Color.BLACK);
}

public Figure(Pen pen, Color color) {


this(new Position(), pen, color);
}

public Figure(Position position, Pen pen, Color color) {


this.pen = (pen != null) ? pen : new Pen();
this.position = (position != null) ? new Position(position.getX(), position.getY()) : new Position();
this.color = (color != null) ? color : Color.BLACK;
}

48
Exemplo – Desenho Casa
▸ Solução 3 – classe derivada Circle
public class Circle extends Figure {

private int radius;

public Circle() {
this.radius = 1;
}

public Circle(int radius) {


this.radius = radius;
}

public Circle(int radius, Position position, Pen pen, Color color) {


super(position, pen, color);
this.radius = radius;
}

// restante código
} 49
Exemplo – Desenho Casa
@Override
▸ Solução 3 – classe public void draw() {
derivada Circle: Pen pen = getPen();
pen.setColor(getColor());
método draw pen.penUp();
pen.moveTo(getX(),getY());
pen.penDown();
pen.turnTo(0);

int sides = 50;


int side = (int) (2 * Math.PI * radius / sides);

for (int i = 0; i < sides + 1; i++) {


pen.move(side);
pen.turn((int) (360.0 / sides));
}
}

50
Exemplo – Desenho Casa
▸ Solução 3 – classe public class Square extends Figure {

derivada Square private int side;

public Square() {
this.side = 1;
}

public Square(int side) {


this.side = side;
}

public Square(int side, Position position, Pen pen, Color color) {


super(position, pen, color);
this.side = side;
}

// restante código
}

51
Exemplo – Desenho Casa
@Override
▸ Solução 3 – classe derivada public void draw() {

Square: método draw Pen pen = getPen();


pen.setColor(getColor());
pen.penUp();
pen.moveTo(getX(),getY());
pen.penDown();
pen.turnTo(0);

for (int i = 0; i < 4; i++) {


pen.move(side);
pen.turn(90);
}
}

52
Exemplo – Desenho casa
public class Drawing {

▸ Solução 3 – Implementar uma classe private ArrayList<Figure> figures;

para representar desenhos public Drawing() {


figures = new ArrayList<>();
▹ Classe Drawing }

public void addFigure(Figure figure) {


if (figure != null) {
figures.add(figure);
Esta classe }
mantém-se }

idêntica.
public void draw() {
for (Figure figure : figures) {
figure.draw();
}
}
}
53
Exemplo – Desenho casa
public class Drawing {

▸ Solução 3 – Implementar uma classe private ArrayList<Figure> figures;


para representar desenhos public Drawing() {

▹ Classe Drawing }
figures = new ArrayList<>();

public void addFigure(Figure figure) {


if (figure != null) {
O polimorfismo figures.add(figure);
manifesta-se no }
método draw. }
Cada figura desenha-se
à sua maneira public void draw() {
for (Figure figure : figures) {
figure.draw();
}
}
}
54
Exemplo – Desenho casa
public class Drawing {

private ArrayList<Figure> figures;


▸ Solução 3 – Implementar uma
public Drawing() {
classe para representar desenhos figures = new ArrayList<>();
}
▹ Classe Drawing
public void addFigure(Figure figure) {
if (figure != null) {
figures.add(figure);
}
Não é necessária a utilização de }
nenhum switch. Com o
polimorfismo é selecionado public void draw() {
automaticamente o método draw for (Figure figure : figures) {
a ser executado figure.draw();
}
}
}
55
Exemplo – Desenho casa
▸ Solução 3 – programa principal
▹ Ex: representar o desenho da casa
public class Program {

public static void main(String[] args) {

Canvas canvas = new Canvas("Casa", 500, 300, Color.WHITE);


Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Square wall = new Square(120, new Position(170, 140), pen, Color.RED);


Square window = new Square(40, new Position(190, 160), pen, Color.BLACK);
Triangle roof = new Triangle(180, 80, new Position(140, 138), pen, Color.GREEN);
Circle sun = new Circle(40, new Position(360, 40), pen, Color.YELLOW);

drawing.addFigure(wall);
drawing.addFigure(window);
drawing.addFigure(roof);
drawing.addFigure(sun);
Código idêntico
drawing.draw();
ao da solução 1
}
} 56
Exemplo – Desenho casa
▸ Solução 3 – Diagrama de classes

57
Programação Orientada por Objetos

Herança

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Herança de classes
▸ Hierarquias de classes

▸ Principio da substituição

2
Exemplo – Rede Social
▸ Requisitos da rede social:
▹ Um pequeno protótipo com a base para o armazenamento e
apresentação de mensagens.

▹ Faz o armazenamento de mensagens de texto e mensagens de


imagem.

▹ As mensagens de texto podem ter várias linhas;

▹ as mensagens de imagem têm uma imagem e uma descrição.

▹ Todas as mensagens devem incluir o seu autor, a altura em que foi


enviada, o número de “gostos” e a lista de comentários.

3
Exemplo – Rede Social
▸ Objetos que pretendemos representar: MessagePost e PhotoPost

4
Exemplo – Rede Social
▸ Classes associadas: MessagePost e PhotoPost

Atributos

Métodos

5
Exemplo – Rede Social
▸ Diagrama de objetos da rede social

6
Exemplo – Rede Social
▸ Diagrama de classes da rede social

7
Exemplo – Rede Social
▸ Classe MessagePost public class MessagePost {

private String username;


private String message;
private long timestamp;
private int likes;
private ArrayList<String> comments;

public MessagePost(String author, String text) {


username = author;
message = text;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}

// continua…
currentTimeMillis ???

8
(System.currentTimeMillis – das bibliotecas do Java)

9
Exemplo – Rede Social
▸ Métodos da classe public void like() {
likes++;
}
MessagePost (1/3) public void unlike() {
if (likes > 0) {
likes--;
}
}

public void addComment(String text) {


comments.add(text);
}

public String getText() {


return message;
}

public long getTimeStamp() {


return timestamp;
}

10
Exemplo – Rede Social
public void display() {
System.out.println(username);
System.out.println(message);
System.out.print(timeString(timestamp));

▸ Métodos da classe if(likes > 0) {


System.out.println(" - " + likes + " people like this.");
MessagePost (2/3) }
else {
System.out.println();
}

if(comments.isEmpty()) {
System.out.println(" No comments.");
}
else {
System.out.println(" " + comments.size() +
" comment(s). Click here to view.");
}
}

11
Exemplo – Rede Social

▸ Métodos da classe private String timeString(long time) {


long current = System.currentTimeMillis();
MessagePost (3/3) long pastMillis = current - time;
long seconds = pastMillis/1000;
long minutes = seconds/60;
if(minutes > 0) {
return minutes + " minutes ago";
}
Tempo passado em else {
milissegundos return seconds + " seconds ago";
}
}

12
Exemplo – Rede Social
public class PhotoPost {
private String username; Nome do ficheiro com a imagem
private String filename;
private String caption;
private long timestamp;
Legenda da imagem
private int likes;
private ArrayList<String> comments;

public PhotoPost(String author, String filename, String caption)


{
username = author;
this.filename = filename;
this.caption = caption;
timestamp = System.currentTimeMillis();
likes = 0; ▸ Classe PhotoPost
comments = new ArrayList<String>();
}

// continua…

13
Exemplo – Rede Social
public void like() { ▸ Métodos da classe
likes++;
} PhotoPost (1/3)
public void unlike() {
if (likes > 0) {
likes--;
}
}
public void addComment(String text) {
comments.add(text);
}
public String getImageFile() {
return filename;
}
public String getCaption() {
return caption;
}

14
Exemplo – Rede Social
public long getTimeStamp() {
return timestamp;
▸ Métodos da classe
}
public void display() {
PhotoPost (2/3)
System.out.println(username);
System.out.println(" [" + filename + "]");
System.out.println(" " + caption);
System.out.print(timeString(timestamp));
if(likes > 0) {
System.out.println(" - " + likes + " people like this.");
}
else {
System.out.println();
}
if(comments.isEmpty()) {
System.out.println(" No comments.");
}
else {
System.out.println(" " + comments.size() + " comment(s). Click here to view.");
}
}

15
Exemplo – Rede Social

private String timeString(long time) { ▸ Métodos da classe


long current = System.currentTimeMillis();
long pastMillis = current - time; PhotoPost (3/3)
long seconds = pastMillis/1000;
long minutes = seconds/60;
if(minutes > 0) {
return minutes + " minutes ago";
}
else {
return seconds + " seconds ago";
}
}

16
Exemplo – Rede Social
public class NewsFeed {
private ArrayList<MessagePost> messages; ▸ Classe NewsFeed
private ArrayList<PhotoPost> photos;

public NewsFeed() {
messages = new ArrayList<MessagePost>();
photos = new ArrayList<PhotoPost>();
}

public void addMessagePost(MessagePost message) {


messages.add(message);
}

public void addPhotoPost(PhotoPost photo) {


photos.add(photo);
}

// continua…

17
Exemplo – Rede Social
// continuação da classe Newsfeed… ▸ Classe NewsFeed
public void show() {
// display all text posts
for(MessagePost message : messages) {
message.display();
System.out.println(); // empty line between posts
}

// display all photos


for(PhotoPost photo : photos) {
photo.display();
System.out.println(); // empty line between posts
}
}
}

18
Exemplo – Rede Social
▸ Problemas do protótipo criado:

▹ Duplicação de código

▹ As classes MessagePost e PhotoPost são bastante parecidas (grande parte do


código é idêntico).

▹ A manutenção do código dá mais trabalho.

▹ Corre-se o risco de se criarem bugs se não se alterar em todos os locais onde


o código está em duplicado.

▹ A classe NewsFeed também tem duplicação de código.


19
Herança de Classes
▸ Herança de Classes
Exemplo – Rede Social
▸ Classes MessagePost e PhotoPost
▹ Problema: Código duplicado

Atributos

Métodos

21
Exemplo – Rede Social
▸ Solução: Herança de classes Constroi-se uma
nova classe (Post)
com o código
comum.

Reescrevem-se as Representa a relação


classes anteriores de herança
para reutilizarem o
código comum
usando a herança

22
Herança de classes
Herança de classes
▸ É uma técnica usada em Programação Orientada por Objetos que vai permitir a reutilização de código.
▹ Define-se uma classe com o código comum.

▹ Criam-se outras classes com base na classe anterior que reutilizam esse código.

▹ A classe criada inicialmente é a superclasse e as classes que vão reutilizar essa classe são as subclasses.

▹ Ao processo de reutilização de uma classe dá-se o nome de Herança

▹ As subclasses vão herdar o código da superclasse

23
Exemplo – Rede Social
▸ Solução: usar a Herança de classes

Herança de classes (receita simples)

1. Define-se uma superclasse : Post


2. Define-se subclasses para MessagePost e PhotoPost
3. Na superclasse definem-se os atributos comuns
4. As subclasses herdam os atributos da superclasse
5. As subclasses adicionam outros atributos
24
Exemplo – Rede Social
▸ Herança de classes em Java
Sem alterações
public class Post {
...
}
Alterar aqui

public class PhotoPost extends Post {


...
}

public class MessagePost extends Post {


...
}

25
Exemplo – Rede Social

public class Post { ▸ Superclasse – Post


private String username;
private long timestamp;
private int likes;
private ArrayList<String> comments;

// construtores e métodos omitidos


}

26
Exemplo – Rede Social
public class MessagePost extends Post {
private String message; ▸ Subclasses –

// construtores e métodos omitidos MessagePost e


}
PhotoPost
public class PhotoPost extends Post {
private String filename;
private String caption;

// construtores e métodos omitidos


}

27
Exemplo – Rede Social
▸ Herança de classes em Java

Atributos
idênticos

28
Exemplo – Rede Social
▸ Herança de classes em Java

Métodos
idênticos

O BlueJ agrupa os métodos nas classes a


que pertencem no menu mas a utilização
em código não tem diferenças.
29
Exemplo – Rede Social
Herança de classes em Java

Mesma
utilização

30
Herança de classes
▸ Herança de classes

▹ A herança de classe define uma relação “is-a” entre classes.


▹ Ex: A classe MessagePost herda da classe Post:
Uma mensagem de texto (MessagePost) é uma (Is-a) mensagem (Post)

▹ Ex: A classe Carro herda da classe Veiculo:


Um carro é um (Is-a) veiculo

▹ Enquanto a composição define uma relação “has-a” entre classes


▹ Ex: A classe Carro contém (has-a) um objeto da classe Motor:
Um carro contém (has-a) motor
31
Hierarquias de classes
▸ A herança de classes leva à formação de hierarquias de classes

32
Herança – Hierarquia de Classes - vocabulário
Animal é superclasse
direta (classe base ou
classe pai) de Mammal e
Bird, e é super classe
Animal
indireta de Dog, Cat e Cow

Mammal é subclasse
direta (classe derivada ou
classe filha) de Animal e
super classe direta (base
Mammal Bird
ou pai) de Dog, Cat e Cow

Dog é subclasse direta


(classe derivada ou classe
filha) de Mammal e Dog Cat Cow
indirecta de Animal

33
Herança
▸ As subclasses ou classes derivadas vão herdar todos os atributos e métodos da superclasse ou classe
base.
▸ As subclasses não herdam os construtores da superclasse

34
Construtores em Herança

▸ Herança de Classes
Herança
▸ As subclasses não herdam os construtores da superclasse
public class Post {
private String username;
private long timestamp;
private int likes;
private ArrayList<String> comments;

public Post(String author) {


username = author;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}

public void like() {


likes++;
}
}

public class MessagePost extends Post {


private String message;
} 36
Herança
▸ As subclasses ou classes derivadas vão herdar todos os atributos e métodos da superclasse ou classe
base.
▸ As subclasses não herdam os construtores da superclasse
▸ Neste caso é necessário lidar com os construtores de uma forma diferente:
▹ Cada subclasse escolhe o construtor que vai usar da superclasse
▹ A escolha é feita no construtor da subclasse utilizando-se o método super() que representa uma chamada ao
construtor da super classe
▹ A escolha do construtor é feita dependendo dos argumentos do método super() Neste caso o número e tipo
dos argumentos deve corresponder ao número e tipo de argumentos do construtor que se pretende utilizar da
superclasse
▹ O método super() deve ser o primeiro método a ser chamado dentro do construtor da subclasse

37
Exemplo – Rede Social
public class Post {
private String username; ▸ Construtores em herança –
private long timestamp;
private int likes; classe Post
private ArrayList<String> comments;

public Post(String author) {


username = author;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}

// métodos omitidos
}

38
Exemplo – Rede Social
▸ Construtores em herança
public class MessagePost extends Post {
private String message; – classe MessagePost
public MessagePost(String author, String text) {
super(author);
message = text;
} Chamada ao
construtor da
// métodos omitidos superclasse (Post)
}

39
Construtores em herança
▸ Construtores em herança
▹ O construtor da subclasse deve incluir sempre uma chamada ao construtor da superclasse
▹ Se não for incluída o compilador coloca automaticamente uma chamada ao construtor sem
argumentos da superclasse: super();
▹ Apenas resulta se a superclasse tiver um construtor sem argumentos

▹ A chamada ao construtor da superclasse deve ser a primeira instrução do construtor da


subclasse.
public class MessagePost extends Post A classe Post é a superclasse da
{
private String message;
classe MessagePost

public MessagePost(String author, String text)


{
super(author);
message = text;
Chamada ao construtor da classe Post
}

// métodos omitidos
Primeira instrução do construtor
}
40
Herança
public class Post {
private String username;
▸ As subclasses não herdam
private long timestamp;
private int likes;
os construtores da
private ArrayList<String> comments;
superclasse
public Post(String author) {
username = author;
timestamp = System.currentTimeMillis();
likes = 0;
comments = new ArrayList<String>();
}
}

public class MessagePost extends Post { Novo construtor


private String message;

public MessagePost(String author) {


super(author);
message = "";
}

}
41
Herança por Extensão

▸ Herança de Classes
Modificadores de acesso do Java
▸ Membros public (símbolo + nos diagramas de classe): Se os membros são declarados como públicos
dentro de uma classe, então estes membros são acessíveis às classes que estão dentro e fora do
pacote onde esta classe é visível. Este é o menos restritivo de todos os modificadores de
acessibilidade.
▸ Membros protected (símbolo # nos diagramas de classe) : se os membros de uma classe são
declarados como protegidos, eles estarão acessíveis a todas as classes do pacote e a todas as
subclasses desta classe em qualquer pacote em que essa classe esteja visível.
▸ Membros default (nenhum símbolo) : quando nenhum modificador de acessibilidade é especificado para
o membro, ele é implicitamente declarado como visibilidade por defeito. Eles são acessíveis apenas
para as outras classes no pacote da classe.
▸ Membros private (símbolo - nos diagramas de classe) : Este é o mais restritivo de todos os
modificadores de acessibilidade. Esses membros são acessíveis apenas dentro desta classe. Eles não
são acessíveis a partir de nenhuma outra classe dentro do pacote da classe.
43
Exemplo – Rede Social
▸ Adição de outro tipo de Posts – EventPost

*métodos omitidos

Nova classe
EventPost.

44
Exemplo – Rede Social
▸ Hierarquias mais complexas
*métodos omitidos

Para classes que


têm em comum
comentários e
likes

45
Exemplo – Rede Social

Benefícios da Herança de classes


• Evita a duplicação de código

• Permite a reutilização de código

• Simplifica a manutenção

• Promove a extensão de classes

46
Principio da substituição

▸ Herança de Classes
Principio da Substituição
Subclasses e Subtipos
▸ Uma classe define um tipo
▸ Uma subclasse define um subtipo
▸ Sempre que é necessário um objeto de uma classe, pode-se usar em vez disso um objeto
de uma subclasse:
▹ Chama-se principio da substituição
▹ Exemplo
Post post = new MessagePost("João", "Olá Mundo");

Guarda um objeto da classe Post Atribui-se um objeto da subclasse MessagePost


48
Principio da Substituição
▸ Principio da substituição aplicado à atribuição de valores

Vehicle v1 = new Vehicle();


Vehicle v2 = new Car();
Vehicle v3 = new Bicycle();

49
Principio da Substituição
public class NewsFeed {
▸ Principio da substituição
public void addPost(Post post) aplicado à passagem de
{
... parâmetros
}
}

PhotoPost photo = new PhotoPost(...);


MessagePost message = new MessagePost(...);

feed.addPost(photo);
Objetos de subclasses
feed.addPost(message);
podem ser usados como
parâmetros atuais para
superclasses

50
Exemplo – Rede Social
public class NewsFeed {
private ArrayList<Post> posts;
▸ Nova classe NewsFeed
public NewsFeed() {
posts = new ArrayList<Post>();
}

public void addPost(Post post) {


posts.add(post);
}

public void show() {


for(Post post : posts) {
post.display();
System.out.println();
}
}
}

51
Exemplo – Rede Social
▸ Diagrama de objetos

52
Exemplo – Rede Social
▸ Diagrama de classes

53
Exemplo – Rede Social
▸ Antiga classe versus nova classe NewsFeed
public class NewsFeed { public class NewsFeed {
private ArrayList<MessagePost> messages; private ArrayList<Post> posts;
private ArrayList<PhotoPost> photos;
public NewsFeed() {
public NewsFeed() {
messages = new ArrayList<MessagePost>();
posts = new ArrayList<Post>();
photos = new ArrayList<PhotoPost>();
} }
public void addMessagePost(MessagePost message) {
messages.add(message); public void addPost(Post post) {
} posts.add(post);
public void addPhotoPost(PhotoPost photo) { }
photos.add(photo);
}
public void show() {
public void show() {
for(Post post : posts) {
for(MessagePost message : messages) {
message.display(); post.display();
System.out.println(); System.out.println();
} }
for(PhotoPost photo : photos) { }
photo.display(); }
System.out.println();
}
}
}
54
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes &


Michael Kölling, Pearson Education Limited, 2016
▹ Capítulo 10

55
Programação Orientada por Objetos

Herança de
classes -
exemplo
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Herança – Exemplo Xadrez

▸ Redefinição de métodos

▸ Herança – Exemplo Formas Geométricas

▸ Generalização versus especialização de classes

▸ A Classe Object
2
Exemplo – Xadrez
▸ Requisitos do protótipo:
▹ Representar os componentes do jogo sem
implementar as regras ou o desenrolar do jogo.
▹ Representar as peças: peão, torre, cavalo, rei,
rainha e bispo.
▹ Representar o tabuleiro de jogo com as posições.
▹ Deve ser possível obter em texto a posição de
cada peça usando a notação algébrica (ex: e5 –
peão na casa e5, ou Te7 – torre na casa e7).

3
Exemplo – Xadrez

Representação 1
▹ Cada peça poderá ser representada
por uma classe
▹ Peças: peão, torre, cavalo, rei, rainha
e bispo.
▹ O tabuleiro de jogo corresponde a
outra classe.

4
Exemplo – Xadrez

Representação 1
▹ Cada peça poderá ser
representada por uma classe
▹ Peças: peão, torre, cavalo, rei,
rainha e bispo.
▹ O tabuleiro de jogo
corresponde a outra classe.

5
Exemplo – Xadrez
▸ Representação 1 – Classes Pawn e Rook
public class Pawn { public class Rook {

private Colour colour; private Colour colour;


private Position position; private Position position;

public Pawn(Colour colour, Position position) { public Rook(Colour colour, Position position) {
this.colour = colour; this.colour = colour;
if (position != null) { if (position != null) {
this.position = position; this.position = position;
} else { } else {
this.position = new Position(); this.position = new Position();
} }
} }

public Colour getColour() { ... } public Colour getColour() { ... }


public Position getPosition() { ... } public Position getPosition() { ... }
public void setPosition(char x, int y) { ... } public void setPosition(char x, int y) { ... }
public void setPosition(Position position) public void setPosition(Position position)
{...} {...}
public void setY(int y) { ... } public void setY(int y) { ... }
public char getX() { ... } public char getX() { ... }
public int getY() { ... } public int getY() { ... }
public String toString() { ... } public String toString() { ... }
public String getName() { ... } public String getName() { ... }
} }
6
Exemplo – Xadrez private void setup() {
▸ Representação 1 – Classe ChessBoard for (char x = 'a'; x <= 'h'; x++) {
pawns.add(new Pawn(Colour.WHITE, new Position(x, 2)));
pawns.add(new Pawn(Colour.BLACK, new Position(x, 7)));
}
int line = 1;
public class Chessboard {
Colour colour = Colour.WHITE;
rooks.add(new Rook(colour, new Position('a', line)));
ArrayList<Pawn> pawns;
ArrayList<Knight> knights; knights.add(new Knight(colour, new Position('b', line)));
bishops.add(new Bishop(colour, new Position('c', line)));
ArrayList<Rook> rooks;
queens.add(new Queen(colour, new Position('d', line)));
ArrayList<Queen> queens;
kings.add(new King(colour, new Position('e', line)));
ArrayList<Bishop> bishops;
bishops.add(new Bishop(colour, new Position('f', line)));
ArrayList<King> kings;
knights.add(new Knight(colour, new Position('g', line)));
public Chessboard() { rooks.add(new Rook(colour, new Position('h', line)));
line = 8;
pawns = new ArrayList<>();
knights = new ArrayList<>(); colour = Colour.BLACK;
rooks = new ArrayList<>(); rooks.add(new Rook(colour, new Position('a', line)));
queens = new ArrayList<>(); knights.add(new Knight(colour, new Position('b', line)));
kings = new ArrayList<>(); bishops.add(new Bishop(colour, new Position('c', line)));
queens.add(new Queen(colour, new Position('d', line)));
bishops = new ArrayList<>();
kings.add(new King(colour, new Position('e', line)));
setup(); bishops.add(new Bishop(colour, new Position('f', line)));
} knights.add(new Knight(colour, new Position('g', line)));
rooks.add(new Rook(colour, new Position('h', line)));
}
}

7
Exemplo – Xadrez

▸ Representação 1 – Problemas da solução encontrada:

▹ Duplicação de código nas classes das peças.

▹ A representação do tabuleiro ficou complexa.

▹ Uma lista de peças por tipo de peça (6 listas no total)

8
Exemplo – Xadrez

Representação 2
▹ Criar uma classe peça que
representa qualquer peça
▹ Usar um atributo PieceType que
determina qual a peça
representada.
▹ O tabuleiro de jogo corresponde a
outra classe.

9
Exemplo – Xadrez
▸ Representação 2 – Classe Piece
public class Piece { public String toString() {
String text = "";
private Colour colour; switch(pieceType){
private Position position; case ROOK:
private PieceType pieceType; text += 'T';
break;
public Piece(PieceType pieceType, case KNIGHT:
Colour colour, Position position) { text += 'C';
this.pieceType = pieceType; break;
this.colour = colour;
case BISHOP:
if (position != null) {
text += 'B';
this.position = position;
} else { break;
this.position = new Position(); case QUEEN:
} text += 'D';
} break;
case KING:
public Colour getColour() { ... } text += 'R';
public PieceType getPieceType() { ... } break;
public void setPieceType(PieceType pieceType) { ... } }
public Position getPosition() { ... } text += position.toString();
public void setPosition(char x, int y) { ... } return text;
public void setPosition(Position position) { ... } }
public void setY(int y) { ... }
public char getX() { ... }
public int getY() { ... } public String getName() { ... }
}
}
10
Exemplo – Xadrez
▸ Representação 2 – private void setup() {

Classe ChessBoard
for (char x = 'a'; x <= 'h'; x++) {
pieces.add(new Piece(PieceType.PAWN, Colour.WHITE, new Position(x, 2)));
pieces.add(new Piece(PieceType.PAWN, Colour.BLACK, new Position(x, 7)));
}
int line = 1;
Colour colour = Colour.WHITE;
pieces.add(new Piece(PieceType.ROOK, colour, new Position('a', line)));
pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('b', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('c', line)));
pieces.add(new Piece(PieceType.QUEEN, colour, new Position('d', line)));
public class Chessboard { pieces.add(new Piece(PieceType.KING, colour, new Position('e', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('f', line)));
ArrayList<Piece> pieces; pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('g', line)));
pieces.add(new Piece(PieceType.ROOK, colour, new Position('h', line)));
public Chessboard() { line = 8;
pieces = new ArrayList<>(); colour = Colour.BLACK;
setup(); pieces.add(new Piece(PieceType.ROOK, colour, new Position('a', line)));
} pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('b', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('c', line)));
pieces.add(new Piece(PieceType.QUEEN, colour, new Position('d', line)));
pieces.add(new Piece(PieceType.KING, colour, new Position('e', line)));
pieces.add(new Piece(PieceType.BISHOP, colour, new Position('f', line)));
pieces.add(new Piece(PieceType.KNIGHT, colour, new Position('g', line)));
pieces.add(new Piece(PieceType.ROOK, colour, new Position('h', line)));
}
}
11
Exemplo – Xadrez

▸ Representação 2 – Problemas da solução encontrada:

▹ Classe Piece complexa. Tem problemas de coesão.

▹ Na classe Chessboard ter-se-á de utilizar vários switch sempre


que se quiser escolher entre os vários tipos de peça.

▹ Exemplo: na movimentação das peças.

12
Exemplo – Xadrez

▸ Solução:

Usar a herança de classes!

13
Exemplo – Xadrez
▸ Requisitos do protótipo:
▹ Representar os componentes do jogo sem
implementar as regras ou o desenrolar do jogo.
▹ Representar as peças: peão, torre, cavalo, rei,
rainha e bispo.
▹ Representar o tabuleiro de jogo com as
posições.
▹ Deve ser possível obter em texto a posição de
cada peça usando a notação algébrica (ex: e5 –
peão na casa e5, ou Te7 – torre na casa e7). 14
Exemplo – Xadrez (3)
▸ Representação 3 – Usar a herança:

▹ Definir uma classe Piece como superclasse.

▹ Inclui os atributos e métodos que são idênticos em todas as peças.

▹ Definir cada uma das peças como uma subclasse da classe Piece

▹ Na classe Chessboard ter uma única lista de peças tirando partido do


principio da substituição.

▹ Exemplo: na movimentação das peças.


15
Exemplo – Xadrez (3)
▸ Solução com
herança de
classes:

16
Exemplo – Xadrez (3)
▸ Exemplo de objetos da representação 1

Atributos
idênticos

Métodos
idênticos

17
Exemplo – Xadrez (3)
public class Piece {

▸ Classe Piece private Colour colour;


private Position position;

public Piece(Colour colour, Position position)


{
this.colour = colour;
if (position != null) {
this.position = position;
} else {
this.position = new Position();
}
}

// restante código omitido


}
18
Exemplo – Xadrez (3)
public Colour getColour() {
return colour;
▸ Classe Piece – métodos }

(1/2) public Position getPosition() {


return new Position(position.getX(), position.getY());
}

public void setPosition(char x, int y) {


position.setX(x);
position.setY(y);
}

public void setPosition(Position position) {


position.setX(position.getX());
position.setY(position.getY());
}

19
Exemplo – Xadrez (3)
public void setY(int y) {
position.setY(y);
}
▸ Classe Piece – métodos (2/2)
public char getX() {
return position.getX();
}

public int getY() {


return position.getY();
Podemos ter o método }
toString a retornar o
texto da posição @Override
public String toString() {
return position.toString();
}

20
Exemplo – Xadrez (3)

▸ Classe Rook

public class Rook extends Piece { Deriva da classe Piece

public Rook(Colour colour, Position position) {


super(colour, position);
}
Chamada ao construtor da classe
Piece (superclasse)
}

21
Classes Pawn e Rook

Exemplo – Xadrez (3)


Redefinição de
Métodos
▸ Herança de classes
Exemplo – Xadrez (3)
▸ Classe Rook
public class Rook extends Piece { Deriva da classe Piece

public Rook(Colour colour, Position position) {


super(colour, position);
}
Chamada ao construtor da classe Piece
(superclasse)
public String getName() {
return "Torre";
}
Este método é diferente em todas as classes das
peças
@Override
public String toString() {
return "T" + super.toString();
O método toString que é herdado
}
escreve apenas a posição da peça. É
} necessário reescrever este método
24
Exemplo – Xadrez (3)
public class Pawn extends Piece{
Deriva da classe Piece ▸ Classe Pawn
public Pawn(Colour colour, Position position) {
super(colour, position);
} Chamada ao construtor da classe
Piece (superclasse)

public String getName() {


return "Torre";
}
Este método é diferente em todas as classes
das peças
@Override
public String toString() {
return "P" + super.toString();
O método toString que é
}
herdado escreve apenas a
posição da peça. É necessário
} reescrever este método
25
Herança – Redefinição de métodos
▸ Por vezes os métodos herdados da superclasse não servem nas subclasses porque estão associados a
comportamentos próprios das subclasses.
▹ Ex: O método toString herdado da classe Piece devolve apenas a posição da peça na notação algébrica. Na classe
da Rook este método deve colocar a letra ‘T’ antes da posição
▸ Neste casos é necessário redefinir (override) esse método.
▹ A palavra @Override que aparece em cima do toString quer dizer que o método seguinte é a redefinição de um método que já
existe.
▸ No entanto é possível reutilizar os métodos da superclasse usando o prefixo super seguido de um ponto e do identificador
do método que se quer utilizar.

@Override Indica que se está a redefinir um método

public String toString() {


return "T" + super.toString();
}
Chamada ao método toString da superclasse
26
Exemplo – Xadrez (3)

▸ Classe Rook – Redefinição do método toString

27
Exemplo – Xadrez (3)
▸ Classes Queen, King, Bishop, Knight
▹ Omitidas: são semelhantes às anteriores public class Chessboard {
▸ Classe Chessboard
ArrayList<Piece> pieces;

public Chessboard() {
pieces = new ArrayList<>();
setup();
Apenas uma lista para }
guardar as várias peças
// métodos omitidos
}

28
private void setup() {

Exemplo –
for (char x = 'a'; x <= 'h'; x++) {
pieces.add(new Pawn(Colour.WHITE, new Position(x, 2)));
pieces.add(new Pawn(Colour.BLACK, new Position(x, 7)));
}
int line = 1;

Xadrez (3)
Colour colour = Colour.WHITE;
pieces.add(new Rook(colour, new Position('a', line)));
pieces.add(new Knight(colour, new Position('b', line)));
pieces.add(new Bishop(colour, new Position('c', line)));
pieces.add(new Queen(colour, new Position('d', line)));
pieces.add(new King(colour, new Position('e', line)));
pieces.add(new Bishop(colour, new Position('f', line)));
pieces.add(new Knight(colour, new Position('g', line)));
pieces.add(new Rook(colour, new Position('h', line)));
▸ Classe Chessboard –
line = 8;
método setup colour = Colour.BLACK;
pieces.add(new Rook(colour, new Position('a', line)));
pieces.add(new Knight(colour, new Position('b', line)));
pieces.add(new Bishop(colour, new Position('c', line)));
pieces.add(new Queen(colour, new Position('d', line)));
A inicialização tem pieces.add(new King(colour, new Position('e', line)));
de ser feita com o pieces.add(new Bishop(colour, new Position('f', line)));
pieces.add(new Knight(colour, new Position('g', line)));
mesmo detalhe pieces.add(new Rook(colour, new Position('h', line)));
}

29
Exemplo – Formas Geométricas

Requisitos do programa:

▸ Desenho de formas geométricas.

▸ Representar apenas círculos e quadrados.

▹ Deve ser possível saber as dimensões e a

posição de cada uma deles.

▹ Deve ser possível desloca-los.

30
Exemplo – Formas Geométricas

radius side

x,y

x,y

Circle Square

31
Exemplo – Formas Geométricas

32
Exemplo – Formas Geométricas
public class Circle { public int getX() {
private int x, y;
return x;
private int radius;
}
public Circle() {
public void setX(int x) {
this.x = 0;
this.x = x;
this.y = 0;
this. radius = 1; }
} public int getY() {
public Circle(int x, int y, int radius) { return y;
this.x = x;
}
this.y = y;
public void setY(int y) {
this. radius = radius;
this.y = y;
}
}
public int getRadius() {
return radius; public void move( int dx, int dy ) {

} x += dx; y += dy;
public void setRadius(int raio) { }
this. radius = radius; }
}

33
Exemplo – Formas Geométricas
public class Square { public int getX() {
private int x, y;
return x;
private int side;
}
public Square() {
this.x = 0; public void setX(int x) {
this.y = 0; this.x = x;
this.side = 1; }
}
public int getY() {
public Square(int x, int y, int side) {
return y;
this.x = x;
this.y = y; }
this.side = side; public void setY(int y) {
}
this.y = y;
public int getSide() {
}
return side;
} public void move( int dx, int dy ) {
public void setSide(int side) { x += dx; y += dy;
this.side = side; }
}
}
34
public class Program {
public static void main(String[] args) {
Circle circle = new Circle(1, 1, 23);

Exemplo – Square square = new Square(0, 0, 4);

System.out.println("Circulo: - Posição (" + circle.getX() +

Formas "," + circle.getY() +


") – Raio: " + circle.getRadius() );

Geométricas System.out.println("Quadrado: - Posição (" + square.getX() +


"," + square.getY() +
") – Lado: " + square.getSide() );
square.move( 2, 2);

System.out.println("Quadrado: - Posição (" + square.getX() +


"," + square.getY() +
") – Lado: " + square.getLado() );
}
}

35
Exemplo – Formas Geométricas
▸ As classes Circle e Square têm em comum alguns dos atributos e métodos

(código duplicado):

▹ 2 atributos (x e y)

▹ 5 getters & setters

▸ A solução é utilizar a herança criando uma superclasse GeometricShape e

definindo Circle e Square como Subclasses.

36
Exemplo – Formas Geométricas

37
Exemplo – Formas Geométricas
public int getX() {

public class GeometricShape { return x;


}
private int x, y;
public void setX(int x) {
this.x = x;
public GeometricShape () { }
x = 0; public int getY() {
y = 0; return y;
}
}
public void setY(int y) {
this.y = y;
public GeometricShape (int x, int y) { }
this.x = x; public void move( int dx, int dy ) {
this.y = y; x += dx; y += dy;
}
}
}
38
Exemplo – Formas public class Circle extends GeometricShape {

Geométricas private int radius;


public Circle() {
super(0, 0);
this.radius = 1;
}
public Circle(int x, int y, int radius) {
super(x, y);
this. radius = radius;
Acrescenta apenas o atributo
}
radius e os métodos seletores e
modificadores associados public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this. radius = radius;
}
}
39
Exemplo – Formas Geométricas
public class Square extends GeometricShape {
private int side;
public Square() {
super(0, 0); Acrescenta apenas o atributo side
this. side = 1; e os métodos seletores e
} modificadores associados
public Square(int x, int y, int side) {
super(x, y);
this. side = side;
}
public int getSide() {
return side;
}
public void setSize(int side) {
this. side = side;
}
} 40
Exemplo – Formas Geométricas
O método main do programa não sofre qualquer alteração

public class Program {


public static void main(String[] args) {
Circle circle = new Circle(1, 1, 23);
Square square = new Square(0, 0, 4);
System.out.println("Circulo: - Posição (" + circulo.getX() +
"," + circle.getY() +
") – Raio: " + circle.getRadius() );
System.out.println("Quadrado: - Posição (" + square.getX() +
E se quisermos criar "," + square.getY() +
uma nova classe para ") – Lado: " + square.getSide() );
representar um square.move( 2, 2);
System.out.println("Quadrado: - Posição (" + square.getX() +
segmento de reta? "," + square.getY() +
") – Lado: " + square.getSide() );
}
}

41
Exemplo – Formas Geométricas

Nova classe definida como


subclasse de
GeometricShape

42
Exemplo – Formas Geométricas
Acrescenta os atributos x2 e y2 para a
representação do segundo ponto do
segmento de retas
public class LineSegment extends GeometricShape {
public int getX2() {
private int x2, y2;
return x2;
public LineSegment() {
}
super(0, 0);
public void setX2(int x2) {
this.x2 = 1;
this.x2 = x2;
this.y2 = 1;
}
}
public int getY2() {
public LineSegment(int x, int y, int x2, int y2) {
return y2;
super(x, y);
}
this.x2 = x2;
Existe um método que public void setY2(int y2) {
this.y2 = y2; é herdado mas não é
this.y2 = y2;
} adequado nesta classe.
Qual? }
}
43
Herança – Redefinição de Métodos
▸ O método:
public void move( int dx, int dy ) {
x += dx; y += dy;
}
▹ Este método não funciona corretamente para objetos da classe LineSegment.
▹ A solução é redefinir este método nesta classe.
▹ Para redefinir um método basta defini-lo novamente no corpo da subclasse:

public class LineSegment extends GeometricShape {


// código omitido

@Override
public void move( int dx, int dy ) {
super.move(dx, dy);
x2 += dx; y2 += dy;
}
}
44
Exemplo – Formas Geométricas

45
Considerações Finais
▸ Herança de classes

46
Herança (is-a) – Generalização vs Especialização

▸ Uma superclasse de outras classes representa a generalização dessas classes


▹ A superclasse é mais genérica que as subclasses
▹ Por exemplo um Vehicle é uma generalização de Bike e Car.

Versus …

▸ Uma subclasse de uma dada classe é uma especialização dessa classe


▹ As subclasses especializam a sua superclasse
▹ A classe Dog é uma especialização da classe Animal.

47
Herança (is-a) – Generalização vs Especialização
▸ Na prática a herança utiliza-se por generalização e por especialização de classes

Generalização Up Top Especialização


Quando, numa aplicação, se Quando já existe classe genérica e se
verifica que várias classes verifica que a classe que se pretende
partilham um conjunto de criar pode ser derivada por herança
atributos e/ou métodos e que a dessa classe e onde essa herança faz
relação de herança pode ser sentido, dizemos que estamos a usar a
aplicada com a criação duma herança por especialização
classe mais genérica que faça
Down
sentido dizemos que estamos a
usar herança por generalização Bottom

48
Herança (is-a) – Generalização vs Especialização

o
lizaçã
a
ner
Ge

49
Herança (is-a) – Generalização vs Especialização

Es
pe
cia
liz

ão

50
Classe Object
▸ A classe Object é uma classe do Java, que está no topo da hierarquia e da
qual todas as outras são subclasses diretas ou indiretas.
▹ Todas as classes são uma especialização de Object, são todas um
tipo de objeto.
▹ Quando uma classe não deriva de outra o compilador de Java coloca-
a a derivar de Object (é acrescentado extends Object).
▹ A classe Object define um conjunto de métodos que são herdados
por todas as classes, entre eles estão os métodos: toString, equals
e hashCode utilizados antes.
▹ Por isso quando se coloca um destes métodos numa classe, na
prática está-se a redefinir o método herdado, sendo então
necessário colocar @Override antes da redefinição

51
Exemplo – Xadrez (3)
▸ Classe Object

52
Bibliografia

▸ Objects First with Java (6th


Edition), David Barnes & Michael
Kölling, Pearson Education Limited,
2016
▹ Capítulo 10

53
Programação Orientada por Objetos

Polimorfismo

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Principio da Substituição
▸ Tipos Estáticos e Tipos Dinâmicos

▸ Polimorfismo
▸ Exemplo Formas Geométricas

2
Principio da Substituição
▸ Polimorfismo

3
Exemplo – Rede Social

▸ Requisitos da rede social:


▹ Um pequeno protótipo com a base para o armazenamento e
apresentação de mensagens.

▹ Faz o armazenamento de mensagens de texto e mensagens de imagem.

▹ As mensagens de texto podem ter várias linhas;

▹ as mensagens de imagem têm uma imagem e uma descrição.

▹ Todas as mensagens devem incluir o seu autor, a altura em que foi


enviada, o número de “gostos” e a lista de comentários.

4
Exemplo – Rede Social
▸ Solução com herança de classes:
Classe base Post
com o código
comum

Classes derivadas
MessagePost e
PhotoPost

5
Exemplo – Rede Social
▸ Exemplo de utilização

O texto da
mensagem não é
mostrado

6
Exemplo – Rede Social
▸ Exemplo de utilização

O método display que


devia mostrar a
mensagem está na
classe Post

7
Exemplo – Rede Social
▸ Exemplo de utilização
public class Post {
private String username;
private long timestamp;
private int likes;
A informação mostrada está
private ArrayList<String> comments; incompleta para as classes
MessagePost e
// restante código omitido PhotoPost
public void display() {
System.out.println(username);
System.out.print(timeString(timestamp));
if(likes > 0) {
System.out.println(" - " + likes + " people like this.");
}
else {
System.out.println();
} Solução:
if(comments.isEmpty()) { Redefinir o método nas
System.out.println(" No comments."); classes derivadas
}
else {
System.out.println(" " + comments.size() +
" comment(s). Click here to view.");
}
}
} 8
Exemplo – Rede Social
public class MessagePost extends Post {
private String message;

▸ Exemplo de utilização
// restantes métodos omitidos

@Override
public void display() {
super.display();
System.out.println(message);
}
}

public class PhotoPost extends Post {


private String filename;
private String caption;
// restantes métodos omitidos
Reutiliza o método
@Override
display herdado public void display() {
super.display();
System.out.println(" [" + filename + "]");
System.out.println(" " + caption);
}
}
9
Exemplo – Rede Social
▸ Exemplo de utilização

Acrescenta o texto da
mensagem no caso de
objetos MessagePost

10
Principio da Substituição
▸ Subclasses e Subtipos
▸ Uma classe define um tipo
▸ Uma subclasse define um subtipo
▸ Sempre que é necessário um objeto de uma classe pode-se usar em vez disso um
objeto duma subclasse:
▹ Chama-se principio da substituição
▹ Exemplo
Post post = new MessagePost("João", "Olá Mundo");

Atribui-se um objeto da subclasse


Guarda um objeto da classe Post
MessagePost
11
Principio da Substituição
▸ Exemplo de utilização

display de
MessagePost

display de PhotoPost

12
Principio da Substituição
▸ Exemplo de utilização

A variável post é do tipo Post mas


pode guardar objetos de tipos
derivados como é o caso com objetos
dos tipos MessagePost e
PhotoPost

13
Principio da Substituição
▸ Exemplo de utilização

▸ O método display que é executado é o que está no objeto guardado


na variável
▹ display do objeto MessagePost mostra o texto da mensagem
▹ display do objeto PhotoPost mostra o ficheiro e a legenda da
imagem

14
Tipos Estáticos e
Dinâmicos
▸ Polimorfismo

15
Exemplo – Rede Social

▸ Novo requisito da rede social adicional:


▹ Os posts de imagem e de texto devem devolver o texto
associado à mensagem.
▹ No caso das mensagens de texto corresponde ao
texto da mensagem.
▹ No caso das mensagens de imagem corresponde à
legenda da imagem.
▹ Cada tipo de mensagem deve definir o texto que
retorna.

16
Exemplo – Rede Social
▸ Classe MessagePost

public class MessagePost extends Post {


private String message;

public MessagePost(String author, String text) {


super(author);
message = text;
}

// métodos omitidos
O método requerido
public String getText() { já existia na classe
return message; MessagePost
}

}
17
Exemplo – Rede Social
▸ Classe PhotoPost

public class PhotoPost extends Post {


private String filename;
private String caption;

public PhotoPost(String author, String filename, String caption) {


super(author);
this.filename = filename;
this.caption = caption;
}
// métodos omitidos
Novo método
public String getText() {
return caption;
}

}
18
Exemplo – Rede Social
▸ Superclasse – Post

public class Post {


private String username;
private long timestamp;
private int likes;
private ArrayList<String> comments;

// construtores e métodos omitidos


}

19
Principio da Substituição
▸ Exemplo de utilização – método getText

Erro???

Não funciona porque o


método getText não
existe na classe base
Post

20
Tipos Estáticos e Tipos dinâmicos

MessagePost p1 = new MessagePost("José", "Olá pessoal"); Qual é o tipo de p1?

Post p2 = new MessagePost ("José", "Olá pessoal"); Qual é o tipo de p2?

21
Tipos Estáticos e Tipos dinâmicos
▸ O tipo declarado de uma variável é o seu tipo estático (static type)
▸ O tipo do objeto referido pela variável é o seu tipo dinâmico (dynamic type)
▸ O compilador verifica sempre se existem erros nos tipos estáticos.
▹ Não é possível referir objetos que não sejam da classe do tipo estático ou de uma das classes
derivadas do mesmo.
▹ Não é possível chamar métodos para uma variável que não existam no seu tipo estático. Ou seja, que
não existam na classe declarada.

Post p2 = new MessagePost ("José", "Olá pessoal");

22
Tipos Estáticos e Tipos dinâmicos

▸ A variável post é do tipo (estático) Post


▸ O método getText não existe na classe Post (o seu tipo estático)
▸ O compilador deteta que o método getText não existe para a classe Post e dá erro de compilação
▹ No caso mostrado o BlueJ acusa o erro quando está a interpretar o código

23
Tipos Estáticos e Tipos dinâmicos
No caso do método
display não há
problema porque existe
na classe base e nas
classes derivadas

Satisfaz a verificação
estática (e dinâmica)
do tipo

24
Tipos Estáticos e Tipos dinâmicos

O método getText
só existe nas
classes derivadas

Não satisfaz a
verificação estática
do tipo

25
Tipos Estáticos e Tipos dinâmicos

▸ Em tempo de execução (runTime)

▸ O método display que é executado é o que está no objeto guardado


na variável (o seu tipo dinâmico)
▹ display do objeto MessagePost mostra o texto da mensagem
▹ display do objeto PhotoPost mostra o ficheiro e a legenda da
imagem

26
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime)

▸ O método display que é executado é o que está no objeto guardado


na variável (o seu tipo dinâmico)
▸ Durante a execução é procurado o método a executar (Method lookup)
de acordo com o objeto que está guardado na variável
▹ … e não com o tipo da variável

27
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

Tipo estático e tipo


dinâmico são iguais:
PhotoPost

O método display a
executar apenas pode
ser o que está em Neste exemplo não
PhotoPost existe herança
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

Tipo estático e tipo


dinâmico são iguais:
PhotoPost

O método display é procurado na


classe do objeto PhotoPost mas
como não é encontrado é procurado Neste exemplo existe
na classe pai Post. Ao ser herança mas não existe
encontrado é executado esse método redefinição do método
29
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

O tipo estático é Post e o


tipo dinâmico é
PhotoPost

O método display é procurado na


Neste exemplo existe
classe do objeto PhotoPost e como
é encontrado é executado herança e existe
redefinição do método
Tipos Estáticos e Tipos dinâmicos
▸ Sumário da procura do método a executar (Method lookup) em tempo de execução (Run time):
1. A variável é acedida
2. É obtido o nome do método a executar
3. É obtida a classe do objeto referido
4. O método a executar é procurado na classe do objeto
5. Se não for encontrado o método, é procurado na sua superclasse
6. O passo anterior é repetido até que o método seja encontrado
7. Os métodos redefinidos aparecem primeiro tendo assim precedência sobre os outros métodos

31
Polimorfismo
▸ Polimorfismo

32
Polimorfismo
▸ Quando executamos um método, o método que é executado depende da classe do objeto que o
executa. Neste caso dizemos que temos polimorfismo de métodos.
▹ Um método várias (poli) formas (Morfo) de resposta
▹ O método display tem várias formas de resposta, dentro da classe MessagePost tem uma
forma, dentro da classe PhotoPost tem outra forma.

▸ Uma variável polimórfica pode guardar objetos de vários tipos

▸ A chamada de métodos em Java é polimórfica.

▹ O método que realmente é chamado depende do tipo do objeto em tempo de execução (o tipo dinâmico)
33
Polimorfismo

▸ Para solucionar este erro ter-se-ia que criar o método getText na classe
Post mesmo que não fizesse nada
public class Post {

// restante código omitido

public String getText(){


return "";
}
}
34
Polimorfismo

▸ Na execução, o método getText executado vai corresponder ao método getText da classe do objeto
public class PhotoPost extends Post {
public class MessagePost extends Post { private String filename;
private String message; private String caption;
// código omitido // código omitido
@Override @Override
public String getText() { public String getText() {
return message; return caption;
} }
} }
35
Polimorfismo
▸ Poliformismo

36
▸ Polimorfismo

Exemplo Formas
geométricas

37
Exemplo – Formas Geométricas
▸ Requisitos do programa:
▹ Desenho de formas geométricas.
▹ Representar círculos, quadrados e retângulos.
▹ Deve ser possível saber as dimensões e a posição de cada um deles.
▹ Deve ser possível deslocá-los.

38
Polimorfismo - exemplo
▸ Exemplo de formas geométricas

raio radiu x,
s y
x,y
x,y
x,y
lad
x2,y o
2
Círculo Retângulo Quadrado

39
Polimorfismo - exemplo

40
Polimorfismo - exemplo public class circle extends Shape {
private int radius;

public class Shape { public circle() { this.radius = 1; }

private int x, y; public circle(int x, int y, int radius) {


super(x, y); this.radius = radius; }
public Shape () {
public int getRadius() { return radius; }
x = 0;
public void setRadius(int radius) { this.radius = radius; }
y = 0;
}
}
public Shape (int x, int y) {
public class Rectangle extends Shape {
this.x = x; private int x2, y2;
this.y = y; public Rectangle() {this.x2 = 0; this.y2 = 0; }
public Rectangle(int x, int y, int x2, int y2) {
} super(x, y); this.x2 = x2;this.y2 = y2; }
public int getX2() { return x2; }
public int getX() {
public void setX2(int x2) { this.x2 = x2; }
return x; public int getY2() { return y2; }
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
this.x = x; public class Square extends Rectangle {
private int side;
}
public Square() {this.side = 1;}
public int getY() {
public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
} public int getSide() {return side;}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side); setY2(getY() - side); }
this.y = y;
@Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
}
41
Polimorfismo - exemplo
▸ Requisitos
▹ Pretende-se criar um desenho com base em formas geométricas.
▹ É necessário calcular a área ocupada pelas formas geométricas

42
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape { public circle() { this.radius = 1; }
private int x, y; public circle(int x, int y, int radius) {
super(x, y); this.radius = radius; }
public Shape () { public int getRadius() { return radius; }
x = 0; public void setRadius(int radius) { this.radius = radius; }
y = 0; }
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; public Rectangle() {this.x2 = 0; this.y2 = 0; }
this.y = y; public Rectangle(int x, int y, int x2, int y2) {

} Podemos adicionar super(x, y); this.x2 = x2;this.y2 = y2; }


public int getX2() { return x2; }
public int getX() { um método getArea public void setX2(int x2) { this.x2 = x2; }
return x; em cada uma das public int getY2() { return y2; }

formas geométricas!!!
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 43
Polimorfismo - exemplo
public class Shape {
public class circle extends Shape {
private int radius;

@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
getArea do
}
círculo
x = 0;
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; public Rectangle() {this.x2 = 0; this.y2 = 0; }
this.y = y; public Rectangle(int x, int y, int x2, int y2) {
super(x, y); this.x2 = x2;this.y2 = y2; }
}
public int getX2() { return x2; }
public int getX() { public void setX2(int x2) { this.x2 = x2; }
return x; public int getY2() { return y2; }
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 44
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape {
@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
x = 0; }
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; @Override
this.y = y; public double getArea() {

} double dx = x2 - getX();

public int getX() { getArea do double dy = y2 - getY();


return Math.abs(dx * dy);
return x; Retângulo }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 45
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape {
@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
x = 0; }
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; @Override
this.y = y; public double getArea() {

} double dx = x2 - getX();
double dy = y2 - getY();
public int getX() {
return Math.abs(dx * dy);
return x; }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
getArea do quadrado private int side;

não é necessária, o
}
public int getY() {
return y;
método herdado serve
} // Restante código omitido
}
public void setY(int y) {
this.y = y;
}
} 46
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
@Override
public class Shape {
public double getArea() {
private int x, y;
return Math.PI*radius*radius;

}
public Shape () {
x = 0; } // Restante código omitido

y = 0;
public class Rectangle extends Shape {
}
private int x2, y2;
public Shape (int x, int y) {

this.x = x; Teremos de adicionar o @Override


public double getArea() {
this.y = y; método getArea à classe double dx = x2 - getX();
} Shape para o double dy = y2 - getY();
public int getX() {
polimorfismo funcionar return Math.abs(dx * dy);
return x; }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
}
public int getY() {

return y;
} } // Restante código omitido

public void setY(int y) {

this.y = y;
}

} 47
public class circle extends Shape {
private int radius;

Polimorfismo - exemplo @Override

public double getArea() {


return Math.PI*radius*radius;
public class Shape { }

private int x, y; } // Restante código omitido

public class Rectangle extends Shape {


public Shape () { private int x2, y2;
x = 0; @Override
public double getArea() {
y = 0;
double dx = x2 - getX();
} double dy = y2 - getY();
public Shape (int x, int y) { return Math.abs(dx * dy);

this.x = x; O método getArea não faz }


} // Restante código omitido
this.y = y; sentido nesta classe mas
} tem de ser criado para o public class Square extends Rectangle {

polimorfismo funcionar.
private int side;

Colocamo-lo a retornar 0.0!


// Restante código omitido
public double getArea() { } // Restante código omitido

return 0.0;
}
} 48
Polimorfismo - exemplo
Usamos o principio da
public class Main{ substituição para guardar
public static void main(String[] args) { as diferentes formas
Shape[] shapes; geométricas no mesmo
shapes = new Shape[6]; array
shapes[0] = new circle(4, 8, 3);
shapes[1] = new circle(28, 10, 4);
shapes[2] = new Rectangle(3, 0, 5, 6);
shapes[3] = new Rectangle(11, 0, 23, 6);
shapes[4] = new Rectangle(18, 0, 21, 4); Calculamos a área
shapes[5] = new Square(13, 2, 2); aplicando o
double totalArea = 0; polimorfismo. Cada
forma geométrica
for (int i=0; i < shapes.length; i++) { retorna a sua área
totalArea += shapes[i].getArea();
}
System.out.println("Area Total: " + totalArea);
}
}
49
Polimorfismo - exemplo
public class Main{
public static void main(String[] args) {
ArrayList<Shape> shapes = new ArrayList<>();
shapes.add(new circle(4, 8, 3));
shapes.add(new circle(28, 10, 4));
shapes.add(new Rectangle(3, 0, 5, 6));
Alternativa
shapes.add(new Rectangle(11, 0, 23, 6)); com listas!
shapes.add(new Rectangle(18, 0, 21, 4));
shapes.add(new Square(13, 2, 2));
double totalArea = 0;

for (Shape shape : shapes) {


totalArea += shape.getArea();
}
System.out.println("Area Total: " + totalArea);
}
}

50
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes &


Michael Kölling, Pearson Education Limited, 2016
▹ Capítulo 11

51
Programação Orientada por Objetos

Polimorfismo

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Principio da Substituição
▸ Tipos Estáticos e Tipos Dinâmicos

▸ Polimorfismo
▸ Exemplo Formas Geométricas

2
Principio da Substituição
▸ Polimorfismo

3
Exemplo – Rede Social

▸ Requisitos da rede social:


▹ Um pequeno protótipo com a base para o armazenamento e
apresentação de mensagens.

▹ Faz o armazenamento de mensagens de texto e mensagens de imagem.

▹ As mensagens de texto podem ter várias linhas;

▹ as mensagens de imagem têm uma imagem e uma descrição.

▹ Todas as mensagens devem incluir o seu autor, a altura em que foi


enviada, o número de “gostos” e a lista de comentários.

4
Exemplo – Rede Social
▸ Solução com herança de classes:
Classe base Post
com o código
comum

Classes derivadas
MessagePost e
PhotoPost

5
Exemplo – Rede Social
▸ Exemplo de utilização

O texto da
mensagem não é
mostrado

6
Exemplo – Rede Social
▸ Exemplo de utilização

O método display que


devia mostrar a
mensagem está na
classe Post

7
Exemplo – Rede Social
▸ Exemplo de utilização
public class Post {
private String username;
private long timestamp;
private int likes;
A informação mostrada está
private ArrayList<String> comments; incompleta para as classes
MessagePost e
// restante código omitido PhotoPost
public void display() {
System.out.println(username);
System.out.print(timeString(timestamp));
if(likes > 0) {
System.out.println(" - " + likes + " people like this.");
}
else {
System.out.println();
} Solução:
if(comments.isEmpty()) { Redefinir o método nas
System.out.println(" No comments."); classes derivadas
}
else {
System.out.println(" " + comments.size() +
" comment(s). Click here to view.");
}
}
} 8
Exemplo – Rede Social
public class MessagePost extends Post {
private String message;

▸ Exemplo de utilização
// restantes métodos omitidos

@Override
public void display() {
super.display();
System.out.println(message);
}
}

public class PhotoPost extends Post {


private String filename;
private String caption;
// restantes métodos omitidos
Reutiliza o método
@Override
display herdado public void display() {
super.display();
System.out.println(" [" + filename + "]");
System.out.println(" " + caption);
}
}
9
Exemplo – Rede Social
▸ Exemplo de utilização

Acrescenta o texto da
mensagem no caso de
objetos MessagePost

10
Principio da Substituição
▸ Subclasses e Subtipos
▸ Uma classe define um tipo
▸ Uma subclasse define um subtipo
▸ Sempre que é necessário um objeto de uma classe pode-se usar em vez disso um
objeto duma subclasse:
▹ Chama-se principio da substituição
▹ Exemplo
Post post = new MessagePost("João", "Olá Mundo");

Atribui-se um objeto da subclasse


Guarda um objeto da classe Post
MessagePost
11
Principio da Substituição
▸ Exemplo de utilização

display de
MessagePost

display de PhotoPost

12
Principio da Substituição
▸ Exemplo de utilização

A variável post é do tipo Post mas


pode guardar objetos de tipos
derivados como é o caso com objetos
dos tipos MessagePost e
PhotoPost

13
Principio da Substituição
▸ Exemplo de utilização

▸ O método display que é executado é o que está no objeto guardado


na variável
▹ display do objeto MessagePost mostra o texto da mensagem
▹ display do objeto PhotoPost mostra o ficheiro e a legenda da
imagem

14
Tipos Estáticos e
Dinâmicos
▸ Polimorfismo

15
Exemplo – Rede Social

▸ Novo requisito da rede social adicional:


▹ Os posts de imagem e de texto devem devolver o texto
associado à mensagem.
▹ No caso das mensagens de texto corresponde ao
texto da mensagem.
▹ No caso das mensagens de imagem corresponde à
legenda da imagem.
▹ Cada tipo de mensagem deve definir o texto que
retorna.

16
Exemplo – Rede Social
▸ Classe MessagePost

public class MessagePost extends Post {


private String message;

public MessagePost(String author, String text) {


super(author);
message = text;
}

// métodos omitidos
O método requerido
public String getText() { já existia na classe
return message; MessagePost
}

}
17
Exemplo – Rede Social
▸ Classe PhotoPost

public class PhotoPost extends Post {


private String filename;
private String caption;

public PhotoPost(String author, String filename, String caption) {


super(author);
this.filename = filename;
this.caption = caption;
}
// métodos omitidos
Novo método
public String getText() {
return caption;
}

}
18
Exemplo – Rede Social
▸ Superclasse – Post

public class Post {


private String username;
private long timestamp;
private int likes;
private ArrayList<String> comments;

// construtores e métodos omitidos


}

19
Principio da Substituição
▸ Exemplo de utilização – método getText

Erro???

Não funciona porque o


método getText não
existe na classe base
Post

20
Tipos Estáticos e Tipos dinâmicos

MessagePost p1 = new MessagePost("José", "Olá pessoal"); Qual é o tipo de p1?

Post p2 = new MessagePost ("José", "Olá pessoal"); Qual é o tipo de p2?

21
Tipos Estáticos e Tipos dinâmicos
▸ O tipo declarado de uma variável é o seu tipo estático (static type)
▸ O tipo do objeto referido pela variável é o seu tipo dinâmico (dynamic type)
▸ O compilador verifica sempre se existem erros nos tipos estáticos.
▹ Não é possível referir objetos que não sejam da classe do tipo estático ou de uma das classes
derivadas do mesmo.
▹ Não é possível chamar métodos para uma variável que não existam no seu tipo estático. Ou seja, que
não existam na classe declarada.

Post p2 = new MessagePost ("José", "Olá pessoal");

22
Tipos Estáticos e Tipos dinâmicos

▸ A variável post é do tipo (estático) Post


▸ O método getText não existe na classe Post (o seu tipo estático)
▸ O compilador deteta que o método getText não existe para a classe Post e dá erro de compilação
▹ No caso mostrado o BlueJ acusa o erro quando está a interpretar o código

23
Tipos Estáticos e Tipos dinâmicos
No caso do método
display não há
problema porque existe
na classe base e nas
classes derivadas

Satisfaz a verificação
estática (e dinâmica)
do tipo

24
Tipos Estáticos e Tipos dinâmicos

O método getText
só existe nas
classes derivadas

Não satisfaz a
verificação estática
do tipo

25
Tipos Estáticos e Tipos dinâmicos

▸ Em tempo de execução (runTime)

▸ O método display que é executado é o que está no objeto guardado


na variável (o seu tipo dinâmico)
▹ display do objeto MessagePost mostra o texto da mensagem
▹ display do objeto PhotoPost mostra o ficheiro e a legenda da
imagem

26
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime)

▸ O método display que é executado é o que está no objeto guardado


na variável (o seu tipo dinâmico)
▸ Durante a execução é procurado o método a executar (Method lookup)
de acordo com o objeto que está guardado na variável
▹ … e não com o tipo da variável

27
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

Tipo estático e tipo


dinâmico são iguais:
PhotoPost

O método display a
executar apenas pode
ser o que está em Neste exemplo não
PhotoPost existe herança
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

Tipo estático e tipo


dinâmico são iguais:
PhotoPost

O método display é procurado na


classe do objeto PhotoPost mas
como não é encontrado é procurado Neste exemplo existe
na classe pai Post. Ao ser herança mas não existe
encontrado é executado esse método redefinição do método
29
Tipos Estáticos e Tipos dinâmicos
▸ Em tempo de execução (runTime) – procura do método a executar:

O tipo estático é Post e o


tipo dinâmico é
PhotoPost

O método display é procurado na


Neste exemplo existe
classe do objeto PhotoPost e como
é encontrado é executado herança e existe
redefinição do método
Tipos Estáticos e Tipos dinâmicos
▸ Sumário da procura do método a executar (Method lookup) em tempo de execução (Run time):
1. A variável é acedida
2. É obtido o nome do método a executar
3. É obtida a classe do objeto referido
4. O método a executar é procurado na classe do objeto
5. Se não for encontrado o método, é procurado na sua superclasse
6. O passo anterior é repetido até que o método seja encontrado
7. Os métodos redefinidos aparecem primeiro tendo assim precedência sobre os outros métodos

31
Polimorfismo
▸ Polimorfismo

32
Polimorfismo
▸ Quando executamos um método, o método que é executado depende da classe do objeto que o
executa. Neste caso dizemos que temos polimorfismo de métodos.
▹ Um método várias (poli) formas (Morfo) de resposta
▹ O método display tem várias formas de resposta, dentro da classe MessagePost tem uma
forma, dentro da classe PhotoPost tem outra forma.

▸ Uma variável polimórfica pode guardar objetos de vários tipos

▸ A chamada de métodos em Java é polimórfica.

▹ O método que realmente é chamado depende do tipo do objeto em tempo de execução (o tipo dinâmico)
33
Polimorfismo

▸ Para solucionar este erro ter-se-ia que criar o método getText na classe
Post mesmo que não fizesse nada
public class Post {

// restante código omitido

public String getText(){


return "";
}
}
34
Polimorfismo

▸ Na execução, o método getText executado vai corresponder ao método getText da classe do objeto
public class PhotoPost extends Post {
public class MessagePost extends Post { private String filename;
private String message; private String caption;
// código omitido // código omitido
@Override @Override
public String getText() { public String getText() {
return message; return caption;
} }
} }
35
Polimorfismo
▸ Poliformismo

36
▸ Polimorfismo

Exemplo Formas
geométricas

37
Exemplo – Formas Geométricas
▸ Requisitos do programa:
▹ Desenho de formas geométricas.
▹ Representar círculos, quadrados e retângulos.
▹ Deve ser possível saber as dimensões e a posição de cada um deles.
▹ Deve ser possível deslocá-los.

38
Polimorfismo - exemplo
▸ Exemplo de formas geométricas

raio radiu x,
s y
x,y
x,y
x,y
lad
x2,y o
2
Círculo Retângulo Quadrado

39
Polimorfismo - exemplo

40
Polimorfismo - exemplo public class circle extends Shape {
private int radius;

public class Shape { public circle() { this.radius = 1; }

private int x, y; public circle(int x, int y, int radius) {


super(x, y); this.radius = radius; }
public Shape () {
public int getRadius() { return radius; }
x = 0;
public void setRadius(int radius) { this.radius = radius; }
y = 0;
}
}
public Shape (int x, int y) {
public class Rectangle extends Shape {
this.x = x; private int x2, y2;
this.y = y; public Rectangle() {this.x2 = 0; this.y2 = 0; }
public Rectangle(int x, int y, int x2, int y2) {
} super(x, y); this.x2 = x2;this.y2 = y2; }
public int getX2() { return x2; }
public int getX() {
public void setX2(int x2) { this.x2 = x2; }
return x; public int getY2() { return y2; }
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
this.x = x; public class Square extends Rectangle {
private int side;
}
public Square() {this.side = 1;}
public int getY() {
public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
} public int getSide() {return side;}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side); setY2(getY() - side); }
this.y = y;
@Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
}
41
Polimorfismo - exemplo
▸ Requisitos
▹ Pretende-se criar um desenho com base em formas geométricas.
▹ É necessário calcular a área ocupada pelas formas geométricas

42
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape { public circle() { this.radius = 1; }
private int x, y; public circle(int x, int y, int radius) {
super(x, y); this.radius = radius; }
public Shape () { public int getRadius() { return radius; }
x = 0; public void setRadius(int radius) { this.radius = radius; }
y = 0; }
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; public Rectangle() {this.x2 = 0; this.y2 = 0; }
this.y = y; public Rectangle(int x, int y, int x2, int y2) {

} Podemos adicionar super(x, y); this.x2 = x2;this.y2 = y2; }


public int getX2() { return x2; }
public int getX() { um método getArea public void setX2(int x2) { this.x2 = x2; }
return x; em cada uma das public int getY2() { return y2; }

formas geométricas!!!
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 43
Polimorfismo - exemplo
public class Shape {
public class circle extends Shape {
private int radius;

@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
getArea do
}
círculo
x = 0;
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; public Rectangle() {this.x2 = 0; this.y2 = 0; }
this.y = y; public Rectangle(int x, int y, int x2, int y2) {
super(x, y); this.x2 = x2;this.y2 = y2; }
}
public int getX2() { return x2; }
public int getX() { public void setX2(int x2) { this.x2 = x2; }
return x; public int getY2() { return y2; }
public void setY2(int y2) { this.y2 = y2; }
}
}
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 44
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape {
@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
x = 0; }
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; @Override
this.y = y; public double getArea() {

} double dx = x2 - getX();

public int getX() { getArea do double dy = y2 - getY();


return Math.abs(dx * dy);
return x; Retângulo }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
} public Square() {this.side = 1;}
public int getY() { public Square(int x, int y, int side) {
return y; super(x, y, x+side, y+side); this.side = side;}
public int getSide() {return side;}
}
public void setSide(int side) { this.side = side;
public void setY(int y) {
setX2(getX() + side);setY2(getY() - side); }
this.y = y; @Override public void setX(int x) { super.setX(x);
} setX2(getX() + side); } ... }
} 45
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
public class Shape {
@Override
private int x, y;
public double getArea() {

return Math.PI*radius*radius;
public Shape () {
x = 0; }
y = 0; } // Restante código omitido
}
public class Rectangle extends Shape {
public Shape (int x, int y) { private int x2, y2;
this.x = x; @Override
this.y = y; public double getArea() {

} double dx = x2 - getX();
double dy = y2 - getY();
public int getX() {
return Math.abs(dx * dy);
return x; }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
getArea do quadrado private int side;

não é necessária, o
}
public int getY() {
return y;
método herdado serve
} // Restante código omitido
}
public void setY(int y) {
this.y = y;
}
} 46
Polimorfismo - exemplo public class circle extends Shape {
private int radius;
@Override
public class Shape {
public double getArea() {
private int x, y;
return Math.PI*radius*radius;

}
public Shape () {
x = 0; } // Restante código omitido

y = 0;
public class Rectangle extends Shape {
}
private int x2, y2;
public Shape (int x, int y) {

this.x = x; Teremos de adicionar o @Override


public double getArea() {
this.y = y; método getArea à classe double dx = x2 - getX();
} Shape para o double dy = y2 - getY();
public int getX() {
polimorfismo funcionar return Math.abs(dx * dy);
return x; }
} } // Restante código omitido
public void setX(int x) {
public class Square extends Rectangle {
this.x = x;
private int side;
}
public int getY() {

return y;
} } // Restante código omitido

public void setY(int y) {

this.y = y;
}

} 47
public class circle extends Shape {
private int radius;

Polimorfismo - exemplo @Override

public double getArea() {


return Math.PI*radius*radius;
public class Shape { }

private int x, y; } // Restante código omitido

public class Rectangle extends Shape {


public Shape () { private int x2, y2;
x = 0; @Override
public double getArea() {
y = 0;
double dx = x2 - getX();
} double dy = y2 - getY();
public Shape (int x, int y) { return Math.abs(dx * dy);

this.x = x; O método getArea não faz }


} // Restante código omitido
this.y = y; sentido nesta classe mas
} tem de ser criado para o public class Square extends Rectangle {

polimorfismo funcionar.
private int side;

Colocamo-lo a retornar 0.0!


// Restante código omitido
public double getArea() { } // Restante código omitido

return 0.0;
}
} 48
Polimorfismo - exemplo
Usamos o principio da
public class Main{ substituição para guardar
public static void main(String[] args) { as diferentes formas
Shape[] shapes; geométricas no mesmo
shapes = new Shape[6]; array
shapes[0] = new circle(4, 8, 3);
shapes[1] = new circle(28, 10, 4);
shapes[2] = new Rectangle(3, 0, 5, 6);
shapes[3] = new Rectangle(11, 0, 23, 6);
shapes[4] = new Rectangle(18, 0, 21, 4); Calculamos a área
shapes[5] = new Square(13, 2, 2); aplicando o
double totalArea = 0; polimorfismo. Cada
forma geométrica
for (int i=0; i < shapes.length; i++) { retorna a sua área
totalArea += shapes[i].getArea();
}
System.out.println("Area Total: " + totalArea);
}
}
49
Polimorfismo - exemplo
public class Main{
public static void main(String[] args) {
ArrayList<Shape> shapes = new ArrayList<>();
shapes.add(new circle(4, 8, 3));
shapes.add(new circle(28, 10, 4));
shapes.add(new Rectangle(3, 0, 5, 6));
Alternativa
shapes.add(new Rectangle(11, 0, 23, 6)); com listas!
shapes.add(new Rectangle(18, 0, 21, 4));
shapes.add(new Square(13, 2, 2));
double totalArea = 0;

for (Shape shape : shapes) {


totalArea += shape.getArea();
}
System.out.println("Area Total: " + totalArea);
}
}

50
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes &


Michael Kölling, Pearson Education Limited, 2016
▹ Capítulo 11

51
Programação Orientada por Objetos

Polimorfismo
- Exemplo
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Exemplo Desenho Casa – Solução 1

▸ Exemplo Desenho Casa – Solução 2

▸ Exemplo Desenho Casa – Herança e Polimorfismo

▸ Organização de Código

2
Desenho Casa –
Solução 1
▸ Polimorfismo

3
Exemplo – Desenho Casa
▸ Requisitos do desenho (Revisitado):
▹ O desenho é composto por quadrados, triângulos, círculos e
retângulos.
▹ Cada uma das formas geométricas tem uma cor e uma
posição.
▹ O desenho reutiliza as classes Pen e Canvas do ambiente
gráfico. Neste caso as figuras são desenhadas apenas com as
linhas de contorno.
▹ Deverá ser possível criar facilmente outros desenhos com as
figuras geométricas existentes (circulo, quadrado, triângulo e
retângulo).
4
Exemplo – Desenho Casa
Foram criadas 2 soluções:

▸ Solução 1:

▹ Classes para cada tipo de forma geométrica: Square, Rectangle,

Circle, Triangle.

▹ Uma classe Figure que pode ser qualquer das figuras existentes.

▹ Uma classe Drawing para o desenho que guarda uma lista de figuras.

5
Exemplo – Desenho casa
▸ Classe Position para representar a posição das figuras:
public class Position { public int getX() {
return x;
private int x; }
private int y;
public int getY() {
public Position() { return y;
x = 0; }
y = 0;
} public void setX(int x) {
this.x = x;
public Position(int x, int y) { }
this.x = x;
this.y = y; public void setY(int y) {
} this.y = y;
}
// continua ao lado... }
6
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Square
public class Square { É necessária uma pen
externa para desenhar o
private Pen pen; quadrado na superfície
private Color color; associada
private Position position;
private int side;

public Square(Pen pen, Color color,Position position, int side) {


this.pen = pen;
this.color = color;
this.position = position;
this.side = side;
}
// restante código
}
7
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Square public class Square {

// restante código
public void draw() {
pen.setColor(color);
pen.penUp();
pen.moveTo(position.getX(),position.getY());
pen.penDown();
Através dos pen.turnTo(0);
movimentos da pen
é possível efetuar o for(int i=0; i<4; i++) {
desenho duma figura pen.move(side);
pen.turn(90);
}
}
}
8
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▸ Classe Circle public class Circle {
private Pen pen;
private Color color;
private Position position;
private int radius;
Vários atributos
em comum com
a classe Square public Circle(Pen pen,Color color,Position position, int radius) {
this.pen = pen;
this.color = color;
this.position = position;
this.radius = radius;
}
// restante código
}
9
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Circle
public class Circle {
// restante código
public void draw() {
pen.setColor(color);
pen.penUp();
pen.moveTo(position.getX(),position.getY());
Estamos a
pen.penDown();
criar círculos pen.turnTo(0);
aproximados
int sides = 50;
por polígonos
int side = (int)(2*Math.PI*radius/sides);
regulares de
for(int i=0; i<sides+1; i++) {
50 lados.
pen.move(side);
pen.turn((int)(360.0/sides));
}
}
}
10
Exemplo – Desenho casa
▸ Solução 1 – Implementar classes para as figuras geométricas
▹ Classe Triangle public class Triangle {
private Pen pen;
private Color color;
private Position position
private int height, width;
Com atributos
// restante código
em comum com
void draw() {
as outras classes pen.penUp();
pen.moveTo(x, y);
pen.penDown();
pen.turnTo(0);

pen.moveTo(x + (width / 2), y - height);


pen.moveTo(x + width, y);
pen.moveTo(x, y);
}
}
11
Exemplo – Desenho casa
public class Rectangle {
▸ Solução 1 – Implementar classes para as figuras private Pen pen;
geométricas private Color color;
private Position position
▹ Classe Rectangle private int height, width;
void draw() {
pen.penUp();
pen.moveTo(x,y);
pen.penDown();
Com atributos pen.turnTo(0);
em comum com pen.move(width);
pen.turn(90);
as outras classes
pen.move(height);
pen.turn(90);
pen.move(width);
pen.turn(90);
pen.move(height);
pen.turn(90);
} // restante código
} 12
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Classe Figure

public class Figure { Tipo enumerado FigureType para


definir o tipo de figura guardado

private FigureType figureType;


private Square square;
Atributos
private Retangulo rectangle;
necessários para
private Triangulo triangle; os vários tipos de
private circle circle; figura que poderão
ser utilizados
// restante código
}

13
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Tipo FigureType

public enum FigureType {

TRIANGLE, CIRCLE, RECTANGLE, SQUARE


}

14
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Classe Figure - Construtores
public Figure(FigureType figureType, Pen pen, Color color,
Position position, int dimension) {
this.figureType = figureType;
if (figureType == FigureType.CIRCLE) {
circle = new circle(pen, color, position, dimension);
} else { Confuso!
square = new Square(pen, color, position, dimension);
}
}
public Figure(FigureType figureType, Pen pen, Color color,
Position position, int height, int width) {
this.figureType = figureType;
if (figureType == FigureType.TRIANGLE) {
triangle = new Triangulo(pen, color, position, height, width);
} else {
rectangle = new Retangulo(pen, color, position, height, width);
}
}
15
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
▹ Classe Figure - draw public void draw() {
switch(figureType) {
case CIRCLE:
Um switch para lidar circle.draw();
com os diferentes tipos break;
case TRIANGLE:
de figura
triangle.draw();
break;
case RECTANGLE:
rectangle.draw();
break;
Muitas vezes as instruções switch são pistas que
nos indicam que poderá existir um problema de case SQUARE:
coesão onde estão representadas várias square.draw();
entidades e em que a solução passa pela break;
herança (e polimorfismo)
}
}
16
Exemplo – Desenho casa
▸ Solução 1 – Implementar uma classe para representar figuras
public class Drawing {
▹ Classe Drawing private ArrayList<Figure> figures;

public Drawing() {
figures = new ArrayList<>();
}

public void addFigure(Figure figure) {


if(figure != null) {
figures.add(figure);
}
}

public void draw() {


for(Figure figure : figures ) {
figure.draw();
}
}
} 17
Exemplo – Desenho casa
▸ Solução 1 – Programa principal
▹ Ex: representar o desenho da casa
public class Program {

public static void main() {


Canvas canvas = new Canvas("House", 500, 300, Color.WHITE);
Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Figure wall = new Figure(FigureType.SQUARE, pen, Color.RED,


new Position(170, 140), 120);
Figure window = new Figure(FigureType.SQUARE, pen, Color.BLACK,
new Position(190, 160), 40);
Figure roof = new Figure(FigureType.TRIANGLE, pen, Color.GREEN,
new Position(140, 138), 80, 180);
Figure sun = new Figure(FigureType.CIRCLE, pen, Color.YELLOW,
new Position(360, 40), 40);
// continua
18
Exemplo – Desenho casa
▸ Solução 1 – Programa principal
▹ Ex: representar o desenho da casa

drawing.addFigure(wall);
drawing.addFigure(window);
drawing.addFigure(roof);
drawing.addFigure(sun);

drawing.draw();
}
}

19
Exemplo – Desenho casa

▹ Solução 1 – Diagrama de classes

20
Desenho Casa –
Solução 2
▸ Polimorfismo

21
Exemplo – Desenho Casa
▸ Requisitos do desenho (Revisitado):
▹ O desenho é composto por quadrados, triângulos, círculos e
retângulos.
▹ Cada uma das formas geométricas tem uma cor e uma
posição.
▹ O desenho reutiliza as classes Pen e Canvas do ambiente
gráfico. Neste caso as figuras são desenhadas apenas com
as linhas de contorno.
▹ Deverá ser possível criar facilmente outros desenhos com as
figuras geométricas existentes (circulo, quadrado, triângulo
e retângulo).
22
Exemplo – Desenho Casa
▸ Foram criadas 2 soluções:
▹ Solução 1:
▹ Classes para cada tipo de forma geométrica: Square, Rectangle,
Circle, Triangle.
▹ Um classe Figure que pode ser qualquer das figuras existentes.
▹ Uma classe Drawing para o desenho que guarda uma lista de figuras.
▹ Solução 2:
▹ Uma classe Figure que representa qualquer figura.
▹ Uma classe Drawing para o desenho que guarda uma lista de figuras.

23
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure
public class Figure { Tipo enumerado TipoFigura para
definir o tipo de figura guardado
private FigureType type;
private Pen pen;
private Position position;
private Color color;
private int height; Atributos
private int width;
necessários para
private int radius;
os vários tipos de
figura que poderão
// restante código
}
ser utilizados

24
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Tipo FigureType

public enum FigureType {

NONE, TRIANGLE, CIRCLE, RECTANGLE, SQUARE


}

25
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - Construtor

public Figure(Pen pen) {


this.pen = pen;
type = FigureType.NONE;
position = new Position();
}

26
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - setCircle e setSquare

public void setCircle(Position position, int radius) {


type = FigureType.CIRCLE;
this.position = position;
this.radius = radius;
}
public void setSquare(Position position, int side) {
type = FigureType.SQUARE;
this.position = position;
this.width = side;
this.height = side;
}
27
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - setRectangle e setTriangle

public void setRectangle(Position position, int width, int height) {


type = FigureType.RECTANGLE;
this.position = position;
this.width = width;
this.height = height;
}
public void setTriangle(Position position, int base, int height) {
type = FigureType.TRIANGLE;
this.position = position;
this.width = base;
this.height = height;
}
28
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - draw
public void draw() {
if (position == null || type == FigureType.NONE || pen == null) {
return;
}

pen.penUp();
pen.moveTo(position.getX(), position.getY());
pen.setColor(color);
pen.penDown();

// continua…

29
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - draw
switch (type) {
case CIRCLE:
drawCircle();
break;
case TRIANGLE:
drawTriangle();
break;
case RECTANGLE:
drawRectangle();
break;
case SQUARE:
drawSquare();
break;
}
}
30
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - drawSquare e drawCircle
private void drawSquare() {
for (int i = 0; i < 4; i++) {
pen.move(width);
pen.turn(90);
}
}
private void drawCircle() {
int sides = 50;
int side = (int) (2 * Math.PI * radius / sides);

for (int i = 0; i < sides + 1; i++) {


pen.move(side);
pen.turn((int) (360.0 / sides));
}
}
31
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - drawRectangle

private void drawRectangle() {


pen.move(width);
pen.turn(90);
pen.move(height);
pen.turn(90);
pen.move(width);
pen.turn(90);
pen.move(height);
pen.turn(90);
}

32
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Figure - drawTriangle

private void drawTriangle() {


int x = position.getX();
int y = position.getY();

pen.moveTo(x + (width / 2), y - height);


pen.moveTo(x + width, y);
pen.moveTo(x, y);
}

33
Exemplo – Desenho casa
▸ Solução 2 – Implementar uma classe para representar figuras
▹ Classe Drawing public class Drawing {
private ArrayList<Figure> figures;

public Drawing() {
figures = new ArrayList<>();
}

public void addFigure(Figure figure) {


if (figure != null) {
figures.add(figure);
}
}
Idêntica à solução 1
public void draw() {
for (Figure figure : figures) {
figure.draw();
}
}
}
34
Exemplo – Desenho casa
▸ Solução 2 – Programa public class Program {
principal
▹ Ex: representar o
public static void main() {
Canvas canvas = new Canvas("House", 500, 300, Color.WHITE);
desenho da casa Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Figure wall = new Figure(pen);


wall.setCor(Color.RED);
wall.setSquare(new Position(170, 140), 120);
drawing.addFigure(wall);

Figure window = new Figure(pen);


window.setCor(Color.BLACK);
window.setSquare(new Position(190, 160), 40);
drawing.addFigure(window);
// continua

35
Exemplo – Desenho casa
▸ Solução 2 – Programa principal
▹ Ex: representar o desenho da casa

Figure roof = new Figure(pen);


roof.setCor(Color.GREEN);
roof.setTriangle(new Position(140, 138), 180, 60);
drawing.addFigure(roof);

Figure sun = new Figure(pen);


sun.setCor(Color.YELLOW);
sun.setCircle(new Position(360, 50), 40);
drawing.addFigure(sun);

drawing.draw();
}

36
Exemplo – Desenho casa

▹ Solução 2 – Diagrama de classes

37
Desenho Casa:
Herança e Polimorfismo
▸ Polimorfismo

38
Exemplo – Desenho Casa
▸ Problemas das soluções apresentadas:
▹ Solução 1
▹ Código duplicado nas várias formas geométricas
▹ Classe Figure com um atributo por cada tipo de forma geométrica
▹ Solução 2
▹ Classe Figure complexa
▹ Classe Figure com problemas de coesão ao representar as várias
formas geométricas

39
Exemplo – Desenho Casa
▸ Solução com herança de classes e polimorfismo:
▹ Criar uma superclasse Figure com os atributos comuns das formas
geométricas
▹ Criar sublasses para cada uma das formas geométricas.
▹ Criar uma classe Drawing que irá guardar uma lista de formas
geométricas tirando partido do Principio da Substituição
▹ Método draw da classe Figure polimórfico.
▹ O resultado da sua execução depende da figura que estiver na lista.

40
Exemplo – Desenho Casa
▸ Classes da solução 1: public class Rectangle {
public class Square {
private Pen pen;
private Pen pen; private Color color;
private Color color; private Position position;
private Position position; private int height, width;
private int side;
// restante código
// restante código Vários atributos
em comum }
}

public class Triangle {


public class Circle {
private Pen pen;
private Pen pen; private Color color;
private Color color; Apenas pen, color e private Position position;
private Position position; position fazem
private int height, width;
private int radius; sentido na classe
Figure
// restante código
// restante código }
}
41
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure
public class Figure {

private Pen pen;


private Position position;
private Color color;

Construtor sem
public Figure() {
argumentos
pen = new Pen();
position = new Position();
color = Color.BLACK;
}

// restante código
}
42
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores
public Figure(Position position,
public Figure() {
Pen pen, Color color) {
pen = new Pen();
if (pen != null) {
position = new Position();
this.pen = pen;
color = Color.BLACK;
} else {
}
this.pen = new Pen();
}
public Figure(Pen pen, Color color) {
if (position != null) {
if (pen != null) {
this.position =
this.pen = pen;
new Position(position.getX(),
} else {
position.getY());
this.pen = new Pen();
} else {
}
this.position = new Position();
position = new Position();
}
if (color != null) {
if (color != null) {
this.color = color;
this.color = color;
} else {
this.color = Color.BLACK; Aqui também existe } else {
this.color = Color.BLACK;
} alguma duplicação
}
} de código }

43
Construtores – this()
public class Clock {
▸ this() nos construtores
private int hours;
▹ Da mesma maneira que se utiliza private int minutes;
private int seconds;
super() para a chamada ao Chamada ao construtor:
public Clock(){ Clock(int hours, int
construtor da classe base é this(0,0,0); minutes, int seconds)
}
possível usar this() para a
public Clock(int hours, int minutes){
chamada doutro construtor da this(hours, minutes, 0);
própria classe. }

▹ Neste caso mantém-se public Clock(int


this.hours =
hours, int minutes, int seconds){
hours;
a obrigatoriedade da this.minutes
this.seconds
= minutes;
= seconds;
instrução this() ser a }

primeira do construtor // […]


}

44
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores
public Figure(Position position,
Pen pen, Color color) {
if (pen != null) {
this.pen = pen;
public Figure() { } else {
this(new Position(), new Pen(), Color.BLACK); this.pen = new Pen();
} }
if (position != null) {
this.position =
public Figure(Pen pen, Color color) {
new Position(position.getX(),
this(new Position(), pen, color); position.getY());
} } else {
this. position = new Position();
}
if (color != null) {
this.color = color;
Assim evita-se a } else {
duplicação de código nos
this.color = Color.BLACK;
construtores }
} 45
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores
public Figure(Position position,
Pen pen, Color color) {
public Figure() { if (pen != null) {
this(new Position(), new Pen(), Color.BLACK); this.pen = pen;
} } else {
this.pen = new Pen();
}
public Figure(Pen pen, Color color) {
if (position != null) {
this(new Position(), pen, color); this.position =
} new Position(position.getX(),
position.getY());
} else {
this. position = new Position();
}
Outra simplificação que
if (color != null) {
pode ser feita é a utilização this.color = color;
do operador ternário em } else {
substituição dos if this.color = Color.BLACK;
}
} 46
Operador ternário
▸ Operador ternário – ?!

valor se a condição for valor se a condição for


Condição lógica falsa
verdadeira

condição ? valor 1 : valor 2

Ø Exemplo: String period = (hours < 12) ? "am" : "pm";

String period;
if (hours < 12) {
Ø Equivalente com ifs: period = "am";
} else {
period = "pm";
}
47
Exemplo – Desenho Casa
▸ Solução 3 – classe base Figure – construtores

public Figure() {
this(new Position(), new Pen(), Color.BLACK);
}

public Figure(Pen pen, Color color) {


this(new Position(), pen, color);
}

public Figure(Position position, Pen pen, Color color) {


this.pen = (pen != null) ? pen : new Pen();
this.position = (position != null) ? new Position(position.getX(), position.getY()) : new Position();
this.color = (color != null) ? color : Color.BLACK;
}

48
Exemplo – Desenho Casa
▸ Solução 3 – classe derivada Circle
public class Circle extends Figure {

private int radius;

public Circle() {
this.radius = 1;
}

public Circle(int radius) {


this.radius = radius;
}

public Circle(int radius, Position position, Pen pen, Color color) {


super(position, pen, color);
this.radius = radius;
}

// restante código
} 49
Exemplo – Desenho Casa
@Override
▸ Solução 3 – classe public void draw() {
derivada Circle: Pen pen = getPen();
pen.setColor(getColor());
método draw pen.penUp();
pen.moveTo(getX(),getY());
pen.penDown();
pen.turnTo(0);

int sides = 50;


int side = (int) (2 * Math.PI * radius / sides);

for (int i = 0; i < sides + 1; i++) {


pen.move(side);
pen.turn((int) (360.0 / sides));
}
}

50
Exemplo – Desenho Casa
▸ Solução 3 – classe public class Square extends Figure {

derivada Square private int side;

public Square() {
this.side = 1;
}

public Square(int side) {


this.side = side;
}

public Square(int side, Position position, Pen pen, Color color) {


super(position, pen, color);
this.side = side;
}

// restante código
}

51
Exemplo – Desenho Casa
@Override
▸ Solução 3 – classe derivada public void draw() {

Square: método draw Pen pen = getPen();


pen.setColor(getColor());
pen.penUp();
pen.moveTo(getX(),getY());
pen.penDown();
pen.turnTo(0);

for (int i = 0; i < 4; i++) {


pen.move(side);
pen.turn(90);
}
}

52
Exemplo – Desenho casa
public class Drawing {

▸ Solução 3 – Implementar uma classe private ArrayList<Figure> figures;

para representar desenhos public Drawing() {


figures = new ArrayList<>();
▹ Classe Drawing }

public void addFigure(Figure figure) {


if (figure != null) {
figures.add(figure);
Esta classe }
mantém-se }

idêntica.
public void draw() {
for (Figure figure : figures) {
figure.draw();
}
}
}
53
Exemplo – Desenho casa
public class Drawing {

▸ Solução 3 – Implementar uma classe private ArrayList<Figure> figures;


para representar desenhos public Drawing() {

▹ Classe Drawing }
figures = new ArrayList<>();

public void addFigure(Figure figure) {


if (figure != null) {
O polimorfismo figures.add(figure);
manifesta-se no }
método draw. }
Cada figura desenha-se
à sua maneira public void draw() {
for (Figure figure : figures) {
figure.draw();
}
}
}
54
Exemplo – Desenho casa
public class Drawing {

private ArrayList<Figure> figures;


▸ Solução 3 – Implementar uma
public Drawing() {
classe para representar desenhos figures = new ArrayList<>();
}
▹ Classe Drawing
public void addFigure(Figure figure) {
if (figure != null) {
figures.add(figure);
}
Não é necessária a utilização de }
nenhum switch. Com o
polimorfismo é selecionado public void draw() {
automaticamente o método draw for (Figure figure : figures) {
a ser executado figure.draw();
}
}
}
55
Exemplo – Desenho casa
▸ Solução 3 – programa principal
▹ Ex: representar o desenho da casa
public class Program {

public static void main(String[] args) {

Canvas canvas = new Canvas("Casa", 500, 300, Color.WHITE);


Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Square wall = new Square(120, new Position(170, 140), pen, Color.RED);


Square window = new Square(40, new Position(190, 160), pen, Color.BLACK);
Triangle roof = new Triangle(180, 80, new Position(140, 138), pen, Color.GREEN);
Circle sun = new Circle(40, new Position(360, 40), pen, Color.YELLOW);

drawing.addFigure(wall);
drawing.addFigure(window);
drawing.addFigure(roof);
drawing.addFigure(sun);
Código idêntico
drawing.draw();
ao da solução 1
}
} 56
Exemplo – Desenho casa
▸ Solução 3 – Diagrama de classes

57
Programação Orientada por Objetos

Classes
Abstratas e
Interfaces
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Exemplo Raposas e Coelhos


▸ Código da Aplicação

▸ Classes e Métodos Abstratos


▸ Interfaces

2
Classes Abstratas e Interfaces

Exemplo raposas e coelhos

3
Exemplo – Foxes and Rabbits
▸ Requisitos da aplicação:
▹ Um simulador da evolução de populações de raposas e coelhos num campo
limitado.
▹ Na simulação os coelhos andam livremente pelo campo enquanto as raposas os
caçam.
▹ O equilíbrio das populações é obtido da seguinte forma:
▹ Quando existem muitos coelhos não falta alimento às raposas e a sua população
cresce rapidamente.
▹ Com muitas raposas a caçar a população de coelhos começa a diminuir levando à
diminuição do alimento das raposas e consequentemente à redução da sua
população.
▹ Com poucas raposas a população de coelhos começa a crescer e o ciclo repete-se

4
Exemplo – Foxes and Rabbits
▸ Simulação:

5
Exemplo – Foxes and Rabbits
▸ Diagrama de classes
da aplicação Foxes
and Rabbits:

6
Exemplo – Foxes and Rabbits

▸ Classes principais da aplicação


Foxes and Rabbits:
▹ Fox – Representa as raposas.
▹ Rabbit – representa os coelhos
▹ Simulator – representa o motor da simulação.

7
Exemplo – Foxes and Rabbits

▸ Restantes classes da aplicação


Foxes and Rabbits:
▹ Field – Representa o campo 2D onde se desenrola a simulação.
▹ Location – uma posição 2D dentro do campo.
▹ Randomizer – fornece a aleatoriedade da simulação
▹ SimulatorView, FieldStats e Counter – fornecem uma
visualização gráfica da simulação

8
Classes Abstratas e Interfaces

Código da aplicação

9
Exemplo – Foxes and Rabbits
▸ Diagrama de classes da aplicação Foxes and Rabbits:

10
Exemplo – Foxes and Rabbits
Idade mínima de reprodução
▸ Classe Rabbit public class Rabbit {

private static final int BREEDING_AGE = 5; Idade máxima atingida


private static final int MAX_AGE = 40;
private static final double BREEDING_PROBABILITY = 0.12;
private static final int MAX_LITTER_SIZE = 4;
Número máximo de filhos

private static final Random rand = Randomizer.getRandom();

private int age;


private boolean alive;
private Location location;
private Field field;

// continua…

11
Exemplo – Foxes and Rabbits
public Rabbit(boolean randomAge, Field field, Location location) {
▸ Classe Rabbit – age = 0;
alive = true;
construtor e método run this.field = field;
Ação principal dos coelhos:
setLocation(location);
if(randomAge) { executada a cada passo da
age = rand.nextInt(MAX_AGE); simulação para cada coelho
}
}

public void run(List<Rabbit> newRabbits) { A lista newRabbits recebida é


incrementAge(); preenchida com os coelhos que
if(alive) { nasceram
giveBirth(newRabbits);
Location newLocation = field.freeAdjacentLocation(location);
if(newLocation != null) {
setLocation(newLocation);
}
else {
setDead();
}
}
}

12
Exemplo – Foxes and Rabbits
public void run(List<Rabbit> newRabbits) {
incrementAge();
▸ Classe Rabbit – método run if(alive) {
giveBirth(newRabbits);
Location newLocation = field.freeAdjacentLocation(location);
if(newLocation != null) {
setLocation(newLocation);
}
else {
setDead();
}
}
}

o O comportamento dos coelhos é definido pelo método run:


n A idade é incrementada a cada passo da simulação
o Nesta altura o coelho pode morrer
n Os coelhos que já tiverem idade podem ter filhos em cada passo da simulação
o Podem nascer coelhos nesta altura
13
Exemplo – Foxes and Rabbits
public boolean isAlive() {
return alive;

▸ Classe Rabbit – métodos


}

public void setDead() {


isAlive, setDead, alive = false;
if(location != null) {
getLocation e setLocation field.clear(location);
location = null;
field = null;
}
}

public Location getLocation() {


return location;
}

private void setLocation(Location newLocation) {


if(location != null) {
field.clear(location);
}
location = newLocation;
field.place(this, newLocation);
}

14
Exemplo – Foxes and Rabbits
private void incrementAge() {
age++;
▸ Classe Rabbit – métodos if(age > MAX_AGE) {
incrementAge e setDead();
}
giveBirth }

private void giveBirth(List<Rabbit> newRabbits) {


List<Location> free = field.getFreeAdjacentLocations(location);
int births = breed();
for(int b = 0; b < births && free.size() > 0; b++) {
Location loc = free.remove(0);
Rabbit young = new Rabbit(false, field, loc);
newRabbits.add(young);
}
}

15
Exemplo – Foxes and Rabbits
private int breed() {
▸ Classe Rabbit – métodos breed, e int births = 0;
canBreed if(canBreed() && rand.nextDouble() <= BREEDING_PROBABILITY) {
births = rand.nextInt(MAX_LITTER_SIZE) + 1;
}
return births;
}

private boolean canBreed() {


return age >= BREEDING_AGE;
}

16
Exemplo – Foxes and Rabbits
Idade mínima de reprodução
public class Fox {

private static final int BREEDING_AGE = 15; Idade máxima atingida


▸ Classe Fox private static final int MAX_AGE = 150;
private static final double BREEDING_PROBABILITY = 0.08;
private static final int MAX_LITTER_SIZE = 2;
private static final int RABBIT_FOOD_VALUE = 9; Número máximo de filhos

Número de passos necessários para


raposa ter de comer novamente

private static final Random rand = Randomizer.getRandom();

private int age;


private boolean alive;
private Location location;
private Field field;
private int foodLevel;
// continua…

17
Exemplo – Foxes and Rabbits
public Fox(boolean randomAge, Field field, Location location) {
▸ Classe Fox – construtor age = 0;
alive = true;
this.field = field;
setLocation(location);
if(randomAge) {
age = rand.nextInt(MAX_AGE);
foodLevel = rand.nextInt(RABBIT_FOOD_VALUE);
}
else {
foodLevel = rand.nextInt(RABBIT_FOOD_VALUE);
}
}

18
Exemplo – Foxes and Rabbits
public void hunt(List<Fox> newFoxes) {
incrementAge(); A lista newFoxes recebida é
incrementHunger(); preenchida com as raposas que
if(alive) { nasceram
giveBirth(newFoxes);

▸ Classe Fox – método hunt


Location newLocation = findFood();
if(newLocation == null) {
newLocation =
field.freeAdjacentLocation(location);
}
if(newLocation != null) {
setLocation(newLocation);
}
else {
setDead(); Ação principal das
} raposas: executada a
}
}
cada passo da
simulação para cada
raposa

19
Exemplo – Foxes and Rabbits
public void hunt(List<Fox> newFoxes) {
incrementAge();
incrementHunger();
if(alive) {
giveBirth(newFoxes);
Location newLocation = findFood();

▸ Classe Fox – método hunt if(newLocation == null) {


newLocation = field.freeAdjacentLocation(location);
}
if(newLocation != null) {
setLocation(newLocation);
}
else {
setDead();
}
}
}

o O comportamento das raposas é definido pelo método hunt:


n As raposas, como os coelhos, podem morrer ou reproduzirem-se
n As raposas ficam com fome
n As raposas procuram comida nas posições adjacentes
20
Exemplo – Foxes and Rabbits
▸ Classe Fox – método private Location findFood() {
List<Location> adjacent = field.adjacentLocations(location);
findFood Iterator<Location> it = adjacent.iterator();
while(it.hasNext()) {
Location where = it.next();
Object animal = field.getObjectAt(where);
if(animal instanceof Rabbit) {
Rabbit rabbit = (Rabbit) animal;
if(rabbit.isAlive()) {
rabbit.setDead();
foodLevel = RABBIT_FOOD_VALUE;
return where;
}
}
}
return null;
}

21
Exemplo – Foxes and Rabbits
▸ Classe Fox – métodos isAlive, public boolean isAlive() {
return alive;
}

setDead, getLocation e private void setDead() {


alive = false;
setLocation if(location != null) {
field.clear(location);
location = null;
field = null;
}
}

public Location getLocation() {


return location;
}

private void setLocation(Location newLocation) {


if(location != null) {
field.clear(location);
}
location = newLocation;
field.place(this, newLocation);
}

22
Exemplo – Foxes and Rabbits
private void incrementAge() {

▸ Classe Fox – métodos age++;


if(age > MAX_AGE) {
setDead();
}
incrementAge, }

incrementHunger e private void incrementHunger() {


foodLevel--;
if(foodLevel <= 0) {
giveBirth }
setDead();

private void giveBirth(List<Fox> newFoxes) {


List<Location> free = field.getFreeAdjacentLocations(location);
int births = breed();
for(int b = 0; b < births && free.size() > 0; b++) {
Location loc = free.remove(0);
Fox young = new Fox(false, field, loc);
newFoxes.add(young);
}
}

23
Exemplo – Foxes and Rabbits

▸ Classe Fox – métodos breed, e


private int breed() {
int births = 0;
if(canBreed() && rand.nextDouble() <= BREEDING_PROBABILITY) {
canBreed births = rand.nextInt(MAX_LITTER_SIZE) + 1;
}
return births;
}

private boolean canBreed() {


return age >= BREEDING_AGE;
}

24
Exemplo – Foxes and Rabbits

▸ Classe Simulator
public class Simulator {

private static final int DEFAULT_WIDTH = 120;


private static final int DEFAULT_DEPTH = 80;
private static final double FOX_CREATION_PROBABILITY = 0.02;
private static final double RABBIT_CREATION_PROBABILITY = 0.08;

private List<Rabbit> rabbits;


private List<Fox> foxes;

private Field field;


private int step;
private SimulatorView view;

// continua…

25
Exemplo – Foxes and Rabbits
▸ Classe Simulator – public Simulator() {
this(DEFAULT_DEPTH, DEFAULT_WIDTH); Utiliza this() para evitar a
} duplicação de código nos
construtores public Simulator(int depth, int width) {
contrutores
if(width <= 0 || depth <= 0) {
System.out.println("The dimensions must be greater than zero.");
System.out.println("Using default values.");
depth = DEFAULT_DEPTH;
width = DEFAULT_WIDTH;
}

rabbits = new ArrayList<Rabbit>();


foxes = new ArrayList<Fox>();
field = new Field(depth, width);

view = new SimulatorView(depth, width);


view.setColor(Rabbit.class, Color.ORANGE);
view.setColor(Fox.class, Color.BLUE);

reset();
}

26
Exemplo – Foxes and Rabbits

public void reset() {
Classe Simulator – métodos step = 0;
rabbits.clear();
reset e populate foxes.clear();
populate();

view.showStatus(step, field);
}

private void populate() {


Random rand = Randomizer.getRandom();
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
Distribuição inicial for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
das raposas e Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
coelhos foxes.add(fox);
}
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Rabbit rabbit = new Rabbit(true, field, location);
rabbits.add(rabbit);
}

}
}
}

27
public void simulateOneStep() {

Exemplo – step++;

Foxes and List<Rabbit> newRabbits = new ArrayList<Rabbit>();


for(Iterator<Rabbit> it = rabbits.iterator(); it.hasNext(); ) {

Rabbits
Rabbit rabbit = it.next();
rabbit.run(newRabbits);
if(! rabbit.isAlive()) {
it.remove();
}


}
Classe Simulator – List<Fox> newFoxes = new ArrayList<Fox>();
método for(Iterator<Fox> it = foxes.iterator(); it.hasNext(); ) {
Fox fox = it.next();
simulateOneStep fox.hunt(newFoxes);
if(! fox.isAlive()) {
it.remove();
}
}
rabbits.addAll(newRabbits);
Passo da simulação: foxes.addAll(newFoxes);
onde se executam as
view.showStatus(step, field);
ações principais
}

28
▸ Classes Abstratas e Interfaces

Classes e Métodos
Abstratos

29
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação:
▹ Mais uma vez existem classes com vários atributos e métodos idênticos
onde se pode aplicar a herança. É o caso das classes Fox e Rabbit
▹ Criação da superclasse Animal com os atributos e métodos comuns que fizerem sentido.
▹ Na simulação temos listas separadas para raposas e coelhos, com a
herança e usando o principio da substituição podemos ter apenas uma lista
de Animal
▹ A cada passo da simulação as raposas caçam e os coelhos correm. Neste
caso poderíamos tirar partido do polimorfismo onde cada um deles faz a sua
ação.

30
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: criação da classe Animal
public class Rabbit {
public class Fox {
private static final int BREEDING_AGE = 5;
private static final int BREEDING_AGE = 15; private static final int MAX_AGE = 40;
private static final int MAX_AGE = 150; private static final double BREEDING_PROBABILITY = 0.12;
private static final double BREEDING_PROBABILITY = 0.08; private static final int MAX_LITTER_SIZE = 4;
private static final int MAX_LITTER_SIZE = 2; Vários atributos
private static final int RABBIT_FOOD_VALUE = 9; idênticos
private static final Random rand =
private static final Random rand = Randomizer.getRandom(); Randomizer.getRandom();

private int age; private int age;


private boolean alive; private boolean alive;
private Location location; private Location location;
private Field field; private Field field;
private int foodLevel;
// continua…
// continua…

v Os atributos alive, location e field podem passar para a classe Animal


v A inicialização destes atributos será feita no construtor da classe Animal
v O atributo age não pode ainda passar porque depende da constante MAX_AGE que é
diferente nas classes. 31
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: criação da classe Animal
public class Fox { public class Rabbit {

private static final int BREEDING_AGE = 15; private static final int BREEDING_AGE = 5;
private static final int MAX_AGE = 150; private static final int MAX_AGE = 40;
private static final double BREEDING_PROBABILITY = 0.08; private static final double BREEDING_PROBABILITY = 0.12;
private static final int MAX_LITTER_SIZE = 2; private static final int MAX_LITTER_SIZE = 4;
private static final int RABBIT_FOOD_VALUE = 9;

private static final Random rand = private static final Random rand =
Randomizer.getRandom(); Randomizer.getRandom();

private int age; private int age;


private boolean alive; private boolean alive;
private Location location; private Location location;
private Field field; private Field field;
private int foodLevel;
public boolean isAlive() { … }
public boolean isAlive() { … } public void setDead() { … }
private void setDead() { … } public Location getLocation() { … }
public Location getLocation() { … } private void setLocation(Location newLocation) { … }
private void setLocation(Location newLocation) { … }
// continua…
// continua…

Ø Os métodos seletores e modificadores associados aos atributos alive, location e field passam também para a classe Animal
Ø Vai ser necessário acrescentar um método getField na classe Animal porque esse atributo é acedido dentro de outros
métodos das classes Fox e Rabbit 32
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: criação da classe Animal
public class Fox { public class Rabbit {

// código omitido // código omitido

private int age; private int age;


private boolean alive; private boolean alive;
private Location location; private Location location;
private Field field; private Field field;
private int foodLevel;
public boolean isAlive() { … }
public boolean isAlive() { … } public void setDead() { … }
private void setDead() { … } public Location getLocation() { … }
public Location getLocation() { … } private void setLocation(Location newLocation) { … }
private void setLocation(Location newLocation) { … }
// continua…
// continua…

Ø A visibilidade do método setLocation é private, se se quiser ter acesso a este método nas subclasses tem de se alterar para
protected pelo menos na classe Animal.
Ø O método setDead é public na classe Rabbit e private na classe Fox porque na classe Fox era necessário aceder a esse método
da classe Rabbit. Neste caso pode-se passar também a protected tendo em conta que não se pretende que o método faça parte da
interface da classe
33
Exemplo – Foxes and Rabbits
▸ Análise e alterações à public class Simulator {

aplicação: classe Simulator private


private
static
static
final
final
int DEFAULT_WIDTH = 120;
int DEFAULT_DEPTH = 80;
private static final double FOX_CREATION_PROBABILITY = 0.02;
private static final double RABBIT_CREATION_PROBABILITY = 0.08;

private List<Rabbit> rabbits;


private List<Fox> foxes; Listas separadas
Com a utilização da herança podemos ter apenas para Fox e Rabbit
private Field field;
um tipo de lista: uma lista de Animal private int step;
private SimulatorView view;

// continua…
private List<Animal> animals;

34
Exemplo – Foxes and Rabbits
public class Simulator {
public void simulateOneStep() {
▸ Análise e alterações à // código omitido
for(Iterator<Animal> it = animals.iterator(); it.hasNext(); )
aplicação: classe {
Animal animal = it.next();
Simulator if(animal instanceOf Rabbit) {
Rabbit rabbit = (Rabbit) animal;
rabbit.run(newAnimals);
}
else if(animal instanceOf Fox) { É necessário identificar o tipo
Fox fox = (Fox) animal; de animal com instanceOf
fox.hunt(newFoxes);
}
else {
System.out.println("found unknown animal");
}
if(!animal.isAlive()) {
it.remove();
}
}

Ø Não conseguimos utilizar o polimorfismo porque as ações da raposa (hunt) e do coelho


(run) são diferentes.
Ø Solução: criar o método act na classe Animal que na classe Fox chama o método hunt e na
classe Rabbit chama o método run.
35
Exemplo – Foxes and Rabbits
public class Simulator {
public void simulateOneStep() {
▸ Análise e alterações à aplicação: // código omitido

for(Iterator<Animal> it = animals.iterator(); it.hasNext(); ) {


classe Simulator Animal animal = it.next();
animal.act(newAnimals);
if(! animal.isAlive()) {
it.remove();
}
}

Ø Solução: criar o método act na classe Animal que na classe Fox chama o método hunt e na classe Rabbit
chama o método run.
Ø Neste caso volta a acontecer o problema: “que código colocar no método act da classe Animal”?
Ø Na realidade a classe Animal é abstrata, não existem “animais” em abstrato mas sim instâncias particulares de animais
como é o caso da raposa ou do coelho.

36
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação:
public class Simulator {
public void simulateOneStep() {
// código omitido
classe Simulator
for(Iterator<Animal> it = animals.iterator(); it.hasNext(); )
{
Animal animal = it.next();
animal.act(newAnimals);
if(! animal.isAlive()) {
it.remove();
}
}

Ø Quando se tem um método para o qual não se pretende definir o código dizemos que é um método abstrato
Ø Em Java é possível criar um método abstrato

37
Métodos abstratos
▸ Métodos abstratos em Java
modificador abstract sem código e a terminar em ;

public abstract void act(List<Animal> newAnimals);

Ø Os métodos abstratos têm o modificador abstract e em vez do código dentro de


chavetas têm em sua substituição um ponto e vírgula
Ø Nestes casos o método abstrato pode ser usado com o polimorfismo

38
Classes abstratas
▸ Classes abstratas em Java
modificador abstract

public abstract class Animal {

Ø Tal como os métodos as classes também podem ser abstratas.


Ø Para uma classe se tornar abstrata tem que se colocar o modificador abstract antes
da palavra class.
Ø As classes abstratas não têm objetos
Ø Neste caso não será possível criar instâncias duma classe abstrata.

39
Classes e métodos abstratos
▸ Regras das classes e métodos abstratos:
▹ Um método abstrato não tem código
▹ Uma classe abstrata não tem instâncias
▹ Se uma classe tiver pelo menos um método abstrato ela terá de ser obrigatoriamente abstrata
▹ Caso contrário existirá um erro de compilação
▸ As classes abstratas são usadas para:
▹ Definir uma classe base da qual não se pretende criar objetos
▹ Definir uma classe incompleta que inclui um conjunto de métodos (concretos) que servem de base à implementação de
classes semelhantes.
▹ Neste caso criam-se métodos abstratos que devem ser definidos nas classes derivadas e assim concluir a
construção da classe desse tipo

▸ Os métodos abstratos são usados para:


▹ Definir comportamentos que se pretende que sejam implementados numa hierarquia de classes
40
Classes e métodos abstratos

▸ Os métodos abstratos criados numa classe são herdados pelas classes derivadas.
▹ Neste caso devem ser redefinidos nas classes derivadas

▹ Se não forem definidos numa classe derivada essa classe passa a ter um método abstrato e
consequentemente deve ser obrigatoriamente abstrata.

41
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: novo método act public class Fox extends Animal {
// código omitido

public abstract class Animal {


classe Animal @Override
abstrata public void act(List<Animal> newFoxes) {
// código omitido // código do método hunt
}
public abstract void act(List<Animal> newAnimals); }
}

métodos act redefinidos nas


método act abstrato subclasses

public class Rabbit extends Animal {


public class Simulator { // código omitido
public void simulateOneStep() {
// código omitido @Override
public void act(List<Animal> newRabbits) {
for(Iterator<Animal> it = animals.iterator(); it.hasNext(); ) { // código do método run
Animal animal = it.next(); }
animal.act(newAnimals); }
if(! animal.isAlive()) { Polimorfismo do método act
it.remove(); a funcionar
}
}

42
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: criação da classe Animal
public class Fox { public class Rabbit {

// código omitido // código omitido


private static final int BREEDING_AGE = 15; private static final int BREEDING_AGE = 5;

private int age; private int age;

private boolean canBreed() { private boolean canBreed() {


return age >= BREEDING_AGE; return age >= BREEDING_AGE;
} }

// continua… // continua…

Ø Alguns métodos não foram colocados na classe Animal porque dependiam de constantes diferentes nas classes
derivadas, é o caso, por exemplo do método canBreed
Ø Neste caso as constantes (e os atributos), ao contrário dos métodos não podem ser redefinidos nas classes derivadas pelo
que não faz sentido serem colocadas na classe base.
Ø Com os métodos abstratos e o polimorfismo pode-se alterar a situação…

43
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: método canBreed public class Fox extends Animal {
// código omitido
private static final int BREEDING_AGE = 15;
método getBreedingAge abstrato @Override
protected int getBreedingAge() {
return BREEDING_AGE;
public abstract class Animal { }
}
// código omitido
private int age;
métodos getBreedingAge redefinidos nas subclasses
private boolean canBreed() {
return age >= getBreedingAge();
} public class Rabbit extends Animal {
private static final int BREEDING_AGE = 5;
protected abstract int getBreedingAge();
} @Override
protected int getBreedingAge() {
return BREEDING_AGE;
}
Polimorfismo do método getBreedingAge a funcionar }

44
Classes Abstratas e Interfaces

Interfaces

45
Exemplo – Foxes and Rabbits
▸ Novas funcionalidades da simulação:
▹ Uma nova funcionalidade poderá ser a adição de mais animais.
▹ Simples de implementar criando uma nova subclasses da classe Animal.
▹ Outra funcionalidade seria a inclusão de predadores humanos.
▹ Podiam ser, por exemplo, caçadores ou apenas colocarem armadilhas
▹ Neste caso os atores da simulação não seriam apenas animais.
▹ Outros atores a incluir podiam ser plantas ou mesmo as condições meteorológicas.
▹ As plantas influenciariam a população de coelhos e o crescimento das plantas seria
influenciado pelas condições atmosféricas
▹ Nos casos anteriores temos novos atores na simulação mais gerais. Uma solução para poder
continuar a tirar partido do polimorfismo do método act seria a criação de uma classe Actor que
servisse de base às classes referidas e que incluísse o método referido como abstrato.
46
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: novas classes Actor e Hunter

47
Exemplo – Foxes and Rabbits
▸ Análise e alterações à aplicação: nova classe Actor e Hunter
Ø Neste caso a classe Actor poderia ser simplesmente:

public abstract class Actor {


public abstract void act(List<Actor> newActors);
public abstract boolean isActive();
}

Ø Esta estrutura permitiria adicionar facilmente outro tipo de atores

Mas uma classe que apenas tem métodos abstratos pode ser
definida usando um novo tipo de dados em Java: as interfaces

48
Interfaces
Palavra reservada interface
Lista de métodos
▸ Interfaces em Java
public interface Actor {
void act(List<Actor> newActors);
boolean isActive();
}

Ø As interfaces são definidas utilizando-se a palavra chave interface


Ø As interfaces declaram um conjunto de métodos.
Ø Os métodos declarados numa interface têm a visibilidade da interface
Ø Não é necessário definir a visibilidade destes métodos
Ø É ainda possível incluir constantes nas interfaces
Ø Neste caso são sempre estáticas e podem-se omitir as palavras static e final
Ø As constantes também têm a mesma tem a visibilidade da interface 49
Interfaces
▸ Interfaces em Java

public interface Actor {


void act(List<Actor> newActors);
boolean isActive();
} Palavra reservada implements

public class Fox extends Animal implements Actor {


//…

Ø As classes podem herdar das interfaces da mesma forma que herdam duma classe.
Ø Neste caso usam a palavra implements em vez de extends
Ø Diz-se que uma classe implementa uma interface porque, neste caso, ela terá de implementar todos
os seus métodos. Caso contrário passará a ser uma classe abstrata.

50
Interfaces
▸ Interfaces em Java

public interface Actor {


void act(List<Actor> newActors);
Lista de interfaces
boolean isActive();
}

public class Fox extends Animal implements Actor, Drawable {


//…

Ø Uma classe pode implementar (herdar de) várias interfaces.


Ø Neste caso colocam-se as várias interfaces a seguir à palavra implements separadas por vírgulas

Ø As classes que implementam várias interfaces na prática terão de implementar todos os métodos
que existem nessas interfaces.

51
Interfaces
▸ As interfaces definem um novo tipo de dados

▹ É possível criar variáveis dum tipo interface:


Exemplo: Actor actor =

▹ Apesar de ser possível criar variáveis do tipo interface não é possível criar valores do tipo interface.
▹ Neste caso uma variável deste tipo apenas pode guardar um objeto duma classe que
implementa essa interface. Uma classe que implementa uma interface é um subtipo dessa
interface.
Exemplo: Actor actor = new Fox();

▹ O polimorfismo está disponível para interfaces da mesma forma que está para classes
52
Interfaces
▸ As interfaces são especificações de comportamentos
▹ Separam as funcionalidades (os seus métodos abstratos) da sua implementação (nas classes que as implementam)
▹ As classes que implementa essas especificações podem escolher a forma de o fazer.

Ø As interfaces podem herdar de outras interfaces


Ø Neste caso é como se adicionassem métodos aos métodos que são herdados
53
Interfaces
▸ Herança múltipla de interfaces

54
Classes abstratas e Interfaces
▸Classes abstratas Ø Interfaces

▹Definem uma entidade abstrata Ø Definem um comportamento

▹Representam entidades abstratas das quais Ø Representam conjuntos de funcionalidades

nunca serão instanciados objetos. sem implementação.

▹Podem ter métodos abstratos e métodos Ø Apenas têm métodos abstratos

concretos Ø Os métodos das interfaces definem um


comportamento que pode ser
▹Os métodos abstratos definem um
implementado por quaisquer classes
comportamento que deve ser implementado na
(pertencendo ou não a uma hierarquia)
hierarquia de classes
55
Bibliografia
▸ Objects First with Java (6th Edition),
David Barnes & Michael Kölling,
Pearson Education Limited, 2016
▹ Capítulo 12

56
Programação Orientada por Objetos

Classes
Abstratas e
Interfaces
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Exemplo Raposas e Coelhos – Melhorias no código

▸ Exemplo Raposas e Coelhos – Nova funcionalidade

▸ Desenho Casa – Melhorias e Nova Funcionalidade

▸ Desenho Casa – Nova Solução

2
Exemplo raposas e
coelhos – Melhorias no
código
▸ Classes Abstratas e Interfaces

3
Exemplo – Foxes and Rabbits

▸ Requisitos da nova aplicação:


▹ Melhorar o código.
▹ Acrescentar um novo ator – o caçador.

4
Exemplo – Foxes and Rabbits
▸ Diagrama de classes da
aplicação Foxes and Rabbits:

5
Exemplo – Foxes and Rabbits
▸ Diagrama de classes da aplicação Foxes and Rabbits:

Pelo diagrama de
classes vê-se que existe
uma dependência entre
a classe Simulator e as
classes Rabbit e Fox

6
Exemplo – Foxes and Rabbits
public class Simulator {
private static final int DEFAULT_WIDTH = 120;
▸ Classe Simulator private static final
private static final
int DEFAULT_DEPTH = 80;
double FOX_CREATION_PROBABILITY = 0.02;
private static final double RABBIT_CREATION_PROBABILITY = 0.08;

private List<Animal> animals;


private Field field;
private int step;
private SimulatorView view;
Estas constantes estão
relacionadas com as public void simulateOneStep() {
classes Rabbit e Fox step++;

List<Animal> newAnimals = new ArrayList<Animal>();


for(Iterator<Animal> it = animals.iterator(); it.hasNext(); ) {
Animal animal = it.next();
animal.act(newAnimals);
if(! animal.isAlive()) {
Este método apenas it.remove();
}
refere a classe Animal }
não tendo ligações a animals.addAll(newAnimals);
Rabbit e/ou a Fox
view.showStatus(step, field);
}
// Continua…

7
Exemplo – Foxes and Rabbits
▸ Classe Simulator
// Continuação da classe Simulator
public void reset() {
step = 0;
animals.clear();
populate();
Para (re)iniciar a
simulação. Não tem as view.showStatus(step, field);
dependências }
encontradas
private void populate() {
Random rand = Randomizer.getRandom();
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
Para criar e distribuir a for(int col = 0; col < field.getWidth(); col++) {
população inicial de if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
raposas e coelhos Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
}
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Aqui estão as Rabbit rabbit = new Rabbit(true, field, location);
animals.add(rabbit);
dependências às }
classes Rabbit e Fox // else leave the location empty.
}
}
}
8
Exemplo – Foxes and Rabbits
▸ Classe Simulator (Dependências)
public class Simulator {
// Código omitido
private static final double FOX_CREATION_PROBABILITY = 0.02;
private static final double RABBIT_CREATION_PROBABILITY = 0.08;

private List<Animal> animals;


private Field field;
private int step;
private SimulatorView view; 1. As constantes estão
private void populate() {
relacionadas com as
Random rand = Randomizer.getRandom(); classes Fox e Rabbit
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
}
2. São criadas
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col); instâncias das
Rabbit rabbit = new Rabbit(true, field, location); classes Fox e
animals.add(rabbit); Rabbit
}
}
}
}
9
Exemplo – Foxes and Rabbits
▸ Classe Simulator (Dependências)
public class Simulator {
// Código omitido
private static final double FOX_CREATION_PROBABILITY = 0.02;
private static final double RABBIT_CREATION_PROBABILITY = 0.08;

private List<Animal> animals;


private Field field;
private int step;
Problema: Como
private SimulatorView view;
eliminar esta
private void populate() { dependência?
Random rand = Randomizer.getRandom();
field.clear();
for(int row = 0; row < field.getDepth(); row++) {
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
} Solução: Criar
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) { uma classe
Location location = new Location(row, col); separada para a
Rabbit rabbit = new Rabbit(true, field, location);
animals.add(rabbit); inicialização
}
}
}
}
10
Exemplo – Foxes and Rabbits
▸ Nova Classe PopulationGenerator
public class PopulationGenerator {
private static final double FOX_CREATION_PROBABILITY = 0.02;
private static final double RABBIT_CREATION_PROBABILITY = 0.08;

public PopulationGenerator(SimulatorView view) {


view.setColor(Rabbit.class, Color.orange);
view.setColor(Fox.class, Color.blue);
}
As constantes
public void populate(Field field, List<Animal>animals) {
Random rand = Randomizer.getRandom();
também são
for(int row = 0; row < field.getDepth(); row++) { transferidas
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Fox fox = new Fox(true, field, location);
animals.add(fox);
} A classe criada
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
recebe o método
Rabbit rabbit = new Rabbit(true, field, location); de inicialização
animals.add(rabbit);
}
}
}
}
}
11
Exemplo – Foxes and Rabbits
▸ Alterada a classe Simulator

public class Simulator {


private static final int DEFAULT_WIDTH = 120;
private static final int DEFAULT_DEPTH = 80;

private List<Animal> animals;


private Field field;
private int step;
private SimulatorView view; Novo atributo populator
private PopulationGenerator populator;

public void reset() {


step = 0;
animals.clear();
field.clear();
populator.populate(field, animals);
Método populate passa a
view.showStatus(step, field); inicializar a distribuição das
} raposas e coelhos
}

12
Exemplo – Foxes and Rabbits
▸ Diagrama de classes da aplicação Foxes and Rabbits:

Sem dependências
a Rabbit e/ou a Fox

Nova classe
PopulationGenerator

13
Exemplo – Foxes and Rabbits
▸ Continuação da análise às Classes Fox e Rabbit
public class Rabbit {
public class Fox {
// código omitido
// código omitido
private void giveBirth(List<Animal> newRabbits) {
private void giveBirth(List<Animal> newFoxes) {
Field field = getField();
Field field = getField(); List<Location> free =
List<Location> free =
field.getFreeAdjacentLocations(getLocation()); field.getFreeAdjacentLocations(getLocation());
int births = breed(); int births = breed();
for(int b=0; b<births && free.size() > 0; b++) { for(int b=0; b<births && free.size() > 0; b++) {
Location loc = free.remove(0); Código Location loc = free.remove(0);
Fox young = new Fox(false, field, loc);
newFoxes.add(young); parecido Rabbit young = new Rabbit(false, field, loc);
newRabbits.add(young);
} }
} }

// continua… // continua…

Ø Embora o código dos métodos seja diferente é possível passa-los para a classe Animal
tirando partido do polimorfismo.
14
Exemplo – Foxes and Rabbits
Novos métodos abstratos
▸ Nova classe Animal public abstract class Animal {

abstract public int getBreedingAge();


abstract public double getBreedingProbability(); createAnimal é abstrato e
abstract public int getMaxLitterSize(); retorna um Animal
abstract protected Animal createAnimal(boolean randomAge,
Field field, Location location);
protected void giveBirth(List<Animal> newborn) {
Field field = getField();
List<Location> free = field.getFreeAdjacentLocations(getLocation());
int births = breed();
for(int b = 0; b < births && free.size() > 0; b++) {
Location loc = free.remove(0);
newborn.add(createAnimal(false, field, loc));
} Usa o método createAnimal que será
} implementado de forma diferente em
Fox e Rabbit
protected int breed() {
int births = 0;
if(canBreed() && rand.nextDouble() <= getBreedingProbability()) {
births = rand.nextInt(getMaxLitterSize()) + 1;
}
return births;
} Usado em giveBirth
}
15
Exemplo – Foxes and Rabbits
▸ Nova classe Rabbit
public class Rabbit extends Animal {
// Restante código omitido
Implementação dos novos
public int getBreedingAge() { métodos abstratos
return BREEDING_AGE;
}

public double getBreedingProbability() {


return BREEDING_PROBABILITY;
}

public int getMaxLitterSize() {


return MAX_LITTER_SIZE;
}

protected Animal createAnimal(boolean randomAge, Field field, Location location) {


return new Rabbit(randomAge, field, location);
}

} O método createAnimal retorna um objeto Rabbit embora o tipo de retorno seja Animal.
Isto é possível pelo princípio da substituição
16
Exemplo raposas e coelhos
– Nova Funcionalidade

Classes Abstratas e Interfaces

17
Exemplo – Foxes and Rabbits
▸ Acrescentar a nova classe Hunter

Ø Neste caso vai introduzir-se a interface Actor como foi sugerido anteriormente

public interface Actor {


void act(List<Actor> newActors);
boolean isActive();
}

Ø A classe Animal passa a implementar a interface Actor


Ø A classe Simulator passa a ter uma lista de Actor em vez de Animal
18
Exemplo – Foxes and Rabbits
public abstract class Animal implements Actor { ▸ Nova classe Animal
abstract public void act(List<Actor> newAnimals);
Implementa a interface Actor
protected void giveBirth(List<Actor> newborn) {
.
Field field = getField(); A lista agora é de Actor
List<Location> free = field.getFreeAdjacentLocations(getLocation());
int births = breed();
for(int b = 0; b < births && free.size() > 0; b++) {
Location loc = free.remove(0); Este método também passa a
newborn.add(createAnimal(false, field, loc)); receber uma lista de Actor
}
}

protected int breed() {


int births = 0;
if(canBreed() && rand.nextDouble() <= getBreedingProbability()) {
births = rand.nextInt(getMaxLitterSize()) + 1;
}
return births;
}
} // restante código omitido
19
Exemplo – Foxes and Rabbits
public class Simulator { ▸ Nova classe Simulator
private List<Actor> animals;
// restantes atributos e constants omitidos
A lista agora é de Actor
public void simulateOneStep() {
step++;
List<Actor> newAnimals = new ArrayList<Actor>();
for(Iterator<Actor> it = animals.iterator(); it.hasNext(); ) {
Actor animal = it.next();
animal.act(newAnimals); Itera os objetos que implementam a
if(! animal.isActive()) {
interface Actor
it.remove();
}
}
animals.addAll(newAnimals);
view.showStatus(step, field); Chama apenas os métodos act e isActive
} definidos na interface Actor

public void reset() {


step = 0;
animals.clear();
field.clear();
populator.populate(field, animals);
view.showStatus(step, field);
}
} // restante código omitido
20
Exemplo – Foxes and Rabbits
▸ Nova classe Hunter public class Hunter implements Actor {
private static final int MAX_KILLS = 4;
private Field field;
private Location location;

public Hunter(Field field, Location location) {


this.field = field;
setLocation(location);
}

public boolean isActive() {


return true;
}

public void setLocation(Location newLocation) {


if(location != null) {
field.clear(location);
}
location = newLocation;
field.place(this, newLocation);
}
// continua…

21
Exemplo – Foxes and Rabbits
// continuação da classe Hunter…
▸ Nova classe Hunter
public void act(List<Actor> newActors) {
int kills = 0;
List<Location> adjacent = field.adjacentLocations(location);
Iterator<Location> it = adjacent.iterator();

while(it.hasNext() && kills < MAX_KILLS) {


Location where = it.next();
Object actor = field.getObjectAt(where);
if(actor instanceof Animal) {
Animal animal = (Animal) actor;
animal.setDead();
Tem de fazer cast para Animal para
kills++;
poder chamar o método setDead
}
}
Location newLocation = field.freeAdjacentLocation(location);
if(newLocation != null) {
setLocation(newLocation);
}
}
}

22
Exemplo – Foxes and Rabbits
public class PopulationGenerator {
private static final double HUNTER_CREATION_PROBABILITY = 0.01; ▸ Nova classe PopulationGenerator
// outras constantes omitidas
public PopulationGenerator(SimulatorView view) {
view.setColor(Hunter.class, Color.red);
// código omitido
} Os caçadores ficam
public void populate(Field field, List<Actor> actors) { com a cor vermelha na
Random rand = Randomizer.getRandom();
for(int row = 0; row < field.getDepth(); row++) {
nova simulação
for(int col = 0; col < field.getWidth(); col++) {
if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Animal fox = new Fox(true, field, location);
actors.add(fox);
}
else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Animal rabbit = new Rabbit(true, field, location);
actors.add(rabbit);
}
else if(rand.nextDouble() <= HUNTER_CREATION_PROBABILITY) {
Location location = new Location(row, col);
Hunter hunter = new Hunter(field, location);
actors.add(hunter);
}
}
}
}
}
23
Exemplo – Foxes and Rabbits
▸ Diagrama de classes da nova versão da aplicação

24
Exemplo – Foxes and Rabbits
▸ Visualização da nova versão da aplicação

25
Desenho Casa –
Melhorias e Nova
Funcionalidade
▸ Classes Abstratas e Interfaces

26
Exemplo – Desenho Casa

▸ Requisitos do desenho (Revisitado):


▹ O programa existente permite desenhar figuras
geométricas.
▹ Pretende-se agora adicionar a representação de
pessoas como um novo tipo de figura

27
Exemplo – Desenho casa
▸ Diagrama de classes da
solução existente

28
Exemplo – Desenho Casa
▸ Classes relacionadas com figuras geométricas da solução existente:
public class Square extends Figure {
public class Figure {
private int side;
private Pen pen;
private Position position; // restante código
private Color color; }

// restante código public class Circle extends Figure {


}
private int radius;

public class Rectangle extends Figure { // restante código


}

private int width;


public class Triangle extends Figure {
private int height;

// restante código private int width;


} private int height;

// restante código
} 29
Exemplo – Desenho casa
public class Drawing { ▸ Solução existente – classe Drawing
private ArrayList<Figure> figures;

public Drawing() {
figures = new ArrayList<>();
}

public void addFigure(Figure figure) {


if (figure != null) {
figures.add(figure);
}
} Usa o polimorfismo do método
draw no desenho das formas
public void draw() { geométricas guardadas na
for (Figure figure : figures) { lista
figure.draw();
}
}
}
30
Exemplo – Desenho Casa
public class Figure {
▸ Solução existente –
classe base Figure
private Pen pen;
private Position position;
private Color color;

public Figure() {
this(new Position(), new Pen(), Color.BLACK);
}

public Figure(Pen pen, Color color) {


this(new Position(), pen, color);
}

public Figure(Position position, Pen pen, Color color) {


this.pen = (pen != null) ? pen : new Pen();
this.position = (position != null) ? new Position(position.getX(),
position.getY()) : new Position();
this.color = (color != null) ? color : Color.BLACK;
}

public void draw() {


} Tendo em conta que se pretende que o método draw seja
// restante código exportado para a hierarquia e que não se querem criar objetos
da classe Figure pode tornar-se este método abstrato
}
31
Exemplo – Desenho Casa
public abstract class Figure { ▹ Solução existente – classe base Figure
private Pen pen;
private Position position; A classe passa a ser
private Color color;
obrigatoriamente
public Figure() { abstrata
this(new Position(), new Pen(), Color.BLACK);
}

public Figure(Pen pen, Color color) {


this(new Position(), pen, color);
}

public Figure(Position position, Pen pen, Color color) {


this.pen = (pen != null) ? pen : new Pen();
this.position = (position != null) ? new Position(position.getX(),
position.getY()) : new Position();
this.color = (color != null) ? color : Color.BLACK;
}

public abstract void draw();


Fica sem código e
// restante código a terminar em
ponto e vírgula
}

32
Exemplo – Desenho casa
▸ Diagrama de classes da solução

Representação
de uma classe
abstrata

33
Exemplo – Desenho casa

▹ Diagrama de classes da nova solução


Nova classe
Person

34
Exemplo – Desenho Casa
public class Person {
▸ Classe Person
private int height;
private int width;
private Position position; Utiliza a pen
private Color color;
private Pen pen; para o desenho

public Person() {
this(new Pen());
}

public Person(Pen pen) {


height = 60;
width = 30;
position = new Position(280, 190);
color = Color.BLACK;
this.pen = (pen != null) ? pen : new Pen();
}

// restante código
}
35
Exemplo – Desenho Casa
▸ Classe Person - método draw
public void draw() {
int bh = (int) (height * 0.7);
int hh = (height - bh) / 2;
int hw = width / 2;
int x = position.getX();
int y = position.getY();

int[] xpoints = {x - 3, x - hw, x - hw, x - (int) (hw * 0.2) - 1, x - (int) (hw * 0.2) - 1, x - hw,
x - hw + (int) (hw * 0.4) + 1, x, x + hw - (int) (hw * 0.4) - 1, x + hw, x + (int) (hw * 0.2) + 1,
x + (int) (hw * 0.2) + 1, x + hw, x + hw, x + 3, x + (int) (hw * 0.6),
x + (int) (hw * 0.6), x + 3, x - 3, x - (int) (hw * 0.6), x - (int) (hw * 0.6)};
int[] ypoints = {y, y + (int) (bh * 0.2), y + (int) (bh * 0.4), y + (int) (bh * 0.2),
y + (int) (bh * 0.5), y + bh, y + bh, y + (int) (bh * 0.65), y + bh, y + bh,
y + (int) (bh * 0.5), y + (int) (bh * 0.2), y + (int) (bh * 0.4), y + (int) (bh * 0.2),
y, y - hh + 3, y - hh - 3, y - hh - hh, y - hh - hh, y - hh - 3, y - hh + 3};

pen.penUp();
pen.moveTo(xpoints[0], ypoints[0]);
pen.penDown(); Pontos de um
polígono com o
for (int i = 1; i < xpoints.length; i++) {
pen.moveTo(xpoints[i], ypoints[i]);
contorno da pessoa
}
pen.moveTo(xpoints[0], ypoints[0]);
}
36
Exemplo – Desenho Casa
public void setSize(int height, int width) {

▸ Classe Person – métodos


this.height = height;
this.width = width;
}
seletores e modificadores public void setColor(Color color) {
if (color != null) { this.color = color; }
}

public Color getColor() { return color; }

public int getX() { return position.getX(); }

public int getY() { return position.getY(); }

public void setX(int x) { position.setX(x); }

public void setY(int y) { position.setY(y); }

public Position getPosition() {


return new Position(position.getX(), position.getY());
}

public void setPosition(Position position) {


if (position != null) {
this.position = new Position(position.getX(), position.getY());
}
}
37
Exemplo – Desenho Casa
▸ Problema
▹ Solução existente
▹ O desenho é representado por uma lista de figuras geométricas
▹ O desenho tira partido do polimorfismo do método draw para o
desenho das várias figuras geométricas
▹ Nova Solução
▹ É necessário incluir a nova classe Person nos desenhos
▹ Uma pessoa não é uma figura geométrica pelo que não deveria ser
incluída na lista de figuras geométricas que compõem um desenho
38
Desenho Casa –
Nova Solução
▸ Classes Abstratas e Interfaces

39
Exemplo – Desenho Casa
Solução
▸ Solução 1:
▹ Criar uma classe base GraphicElement e fazer as classes Figure e
Person herdarem desta classe. O desenho passaria a ser uma lista de
elementos gráficos.
▹ Poderia ser uma boa solução se quiséssemos apenas utilizar a classe
Person apenas para os desenhos.
▸ Solução 2
▹ Criar uma interface Drawable com o método draw e implementar esta
interface nas classes Figure e Person.
▹ Vamos optar por esta solução.
40
Exemplo – Novo Desenho Casa
▸ Interface Drawable

public interface Drawable {


public void draw();
}

Ø Basta incluir o método draw


Ø é a funcionalidade que queremos ter nos objetos que irão aparecer nos desenhos
Ø Todas as classes que implementarem o método desenhar terão esta nova
funcionalidade

41
Exemplo – Novo Desenho Casa
▸ Classe Person public class Person implements Drawable {

private int height;


private int width;
Implementa a interface
private Position position;
Drawable
private Color color;
private Pen pen;

@Override
public void draw() {
// código omitido
}
Tem que implementar o
método draw da interface
// restante código Drawable
}

42
Exemplo – Novo Desenho Casa
▸ Classe base Figure
public abstract class Figure implements Drawable {

private Pen pen;


private Position position;
private Color color; Implementa a interface
Drawable

// public void draw() {


// }
Neste caso não é necessário
// restante código colocar o método draw da
interface
}

Ø Como o método draw não é implementado a classe fica com o método da interface que é abstrato,
torna-se abstrata porque não o implementa e deixa para as classes derivadas a sua implementação.
43
Exemplo – Novo Desenho Casa
public class Drawing {
▸ Nova classe Drawing
private ArrayList<Drawable> figures;

public Drawing() { Passa a definir uma


figures = new ArrayList<>(); lista de Drawable
}

public void addFigure(Drawable figure) {


if (figure != null) {
figures.add(figure); Usa o polimorfismo com
objetos que pertencem a
}
classes que implementam a
} interface Drawable

public void draw() {


for (Drawable figure : figures) {
figure.draw();
}
}
}
44
Exemplo – Novo Desenho Casa
public static void main() {
▸ método main
Canvas canvas = new Canvas("Casa", 500, 300, Color.WHITE);
Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Square wall = new Square(120, new Position(170, 140), pen, Color.RED);


Square window = new Square(40, new Position(190, 160), pen, Color.BLACK);
Triangle roof = new Triangle(180, 80, new Position(140, 138), pen, Color.GREEN);
Circle sun = new Circle(40, new Position(360, 40), pen, Color.YELLOW);

Person person = new Person(pen);


person.setPosition(new Position(340,210));

drawing.addFigure(wall); Criada a person


drawing.addFigure(window);
drawing.addFigure(roof);
drawing.addFigure(sun);
drawing.addFigure(person);
Adicionada ao
drawing.draw(); desenho
}

45
Exemplo – Novo Desenho Casa
▸ método main
public static void main() {

Canvas canvas = new Canvas("Casa", 500, 300, Color.WHITE);


Pen pen = new Pen(0, 0, canvas);
Drawing drawing = new Drawing();

Square wall = new Square(120, new Position(170, 140), pen, Color.RED);


Square window = new Square(40, new Position(190, 160), pen, Color.BLACK);
Triangle roof = new Triangle(180, 80, new Position(140, 138), pen, Color.GREEN);
Circle sun = new Circle(40, new Position(360, 40), pen, Color.YELLOW);

Person person = new Person(pen);


person.setPosition(new Position(340,210));

drawing.addFigure(wall);
drawing.addFigure(window);
drawing.addFigure(roof);
drawing.addFigure(sun);
drawing.addFigure(person);

drawing.draw();
}

46
Exemplo – Novo Desenho Casa
▸ Diagrama de classes do novo desenho casa
Representação
da interface

47
Bibliografia

▸ Objects First with Java (6th


Edition), David Barnes & Michael
Kölling, Pearson Education
Limited, 2016
▹ Capítulo 12

48
Programação Orientada por Objetos

Coleções do
Java
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Coleções e Listas
▸ Conjuntos

▸ Mapas
▸ Exemplo Escola, Algoritmos
2
Coleções e Listas
▸ Coleções
Coleções e a Java Collections Framework
▸ Coleções: permitem agrupar vários elementos
▸ Java Collections Framework (JCF):
▹ É uma arquitetura unificada que inclui interfaces, classes (abstratas e concretas) e
algoritmos (implementados por métodos).
▹ A JCF inclui quatro tipos principais de coleções:
▹ Conjuntos (Set): Coleção de elementos sem ordem e sem elementos repetidos
▹ Listas (List): Coleção de elementos ordenados e com possíveis repetições
▹ Mapas (Map): Coleção de pares chave-valor, sem repetição da chave
▹ Filas (Queue) : Sequências de elementos com diferentes critérios de inserção e remoção
4
Breve introdução às Interfaces em Java
▸ Como uma classe, uma interface pode ter métodos e variáveis, mas os métodos
declarados numa interface são abstratas por defeito (incluem apenas a assinatura
de método, sem corpo).
▸ As interfaces especificam o que uma classe deve fazer e não como.
▹ É o papel da classe implementar o método.
▸ Uma Interface é sobre funcionalidades como um jogador (Player) pode ser uma
interface e qualquer classe que implemente Player deve ser capaz de (ou deve
implementar) o método move().
▸ Portanto, a interface especifica um conjunto de métodos que a classe deve
implementar.
5
Interfaces em Java
▸ Se uma classe implementa uma interface e não fornece corpos de método para

todas as funções especificadas na interface, a classe deve ser declarada abstrata.

▸ Um exemplo de biblioteca Java é a Interface Comparator.

▹ Se uma classe implementa essa interface, ela pode ser usada para ordenar

uma coleção.

6
Sintaxe interface <nome_da_interface> {
// declare as constantes
// declare os métodos que são abstratos
// por defeito.
}

▸ Para declarar uma interface, usa-se a palavra-chave interface.


▸ É usada para fornecer abstração total.
▹ Isso significa que todos os métodos numa interface são declarados com um corpo
vazio e são públicos e todos os atributos são públicos, estáticos e finais por defeito.
▸ Uma classe que implementa uma interface deve implementar todos os métodos declarados
na interface. Para implementar a interface, usa-se a palavra-chave implements.
7
class Bicycle implements Vehicle{

Exemplo
int speed;
int gear;

// para mudar de velocidade


@Override
public void changeGear(int newGear){

gear = newGear;
}
interface Vehicle {
// para acelerar
@Override
// all are the abstract methods. public void speedUp(int increment){
public void changeGear(int a);
speed = speed + increment;
public void speedUp(int a); }
public void applyBrakes(int a);
} // para decelerar
@Override
public void applyBrakes(int decrement){

speed = speed - decrement;


}

public void printStates() {


System.out.println("speed: " + speed + " gear: " + gear);
}
}

8
Java Collections Framework – Interfaces Genéricas

▸ As coleções da JCF são definidas através de interfaces. Neste caso cada uma das interfaces
estabelece os métodos que um determinado tipo de coleção deve ter.
9
Java Collections Framework – Interfaces, classes abstratas e classes

10
Java Collections Framework – Interfaces Genéricas
A JCF define interfaces genéricas permitindo que se possa utilizar nas coleções um determinado tipo de
dados escolhido pelo programador.
▸ Exemplos:
▹ public interface Collection<E> …
▹ Coleção de elementos do tipo ‘E’lement
▹ public interface Map<K,V> …
▹ Mapa de elementos do tipo ‘V’alue com chave do tipo ‘K’ey.

11
Coleções – Interface Collection<E>

▸ Collection<E> é a interface base de


grande parte das coleções.
▹ Significa que os métodos desta interface
serão implementados por todas as
coleções.

12
Coleções – Interface List<E>
▸ List<E> para coleções com elementos em
sequência.
▹ Os elementos nas listas estão ordenados
▹ As listas podem ter elementos duplicados.
▹ O cliente de uma Lista tem, normalmente, controlo
sobre a posição onde um elemento é inserido.
▹ O acesso a um elemento é feito por um índice
(referência de posição).

13
Coleções – ArrayList<E>
▸ ArrayList<E> – é a implementação da interface List<E> mais comum e que possui um bom
desempenho especialmente no acesso aos dados e nas operações de iteração.
▹ A implementação interna utiliza arrays.
▹ ArrayList<Person> persons = new ArrayList<>();
▹ Declara e cria um ArrayList chamado persons para armazenar objetos da classe Person
▸ A JCF define as coleções através de interfaces e disponibiliza várias classes que implementam
essas coleções.
▹ Neste caso a classe genérica ArrayList<E> é uma das implementações disponíveis na
JCF para a interface List<E>

14
Exemplo – Utilização de listas

15
Coleções – ArrayList<E>
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("IPOO");
list.add("POO");
▸ Exemplo de utilização: list.add("POO");
list.add("IPOO");
for(int i = 0; i <list.size(); ++i) {
if (list.get(i).equals("POO")) {
list.remove(i);

}
}
list.remove("IPOO");
for(int i = 0; i <list.size(); ++i) {
System.out.println(list.get(i));
}
}

16
Coleções – LinkedList<E>
▸ LinkedList<E> – É outra implementação da interface List<E>. Nas operações de
inserção e remoção de elementos pode oferecer melhor desempenho do que a anterior.

LinkedList<Person> persons = new LinkedList<>();


▹ Declara e cria uma LinkedList chamada persons para armazenar objetos da classe Person

▸ Internamente esta implementação não utiliza arrays, para não "desperdiçar" espaço, mas

regista, para cada elemento qual o próximo elemento e qual o elemento anterior, sendo então
fácil percorrer a lista e inserir e/ou remover elementos.

17
Exemplo – Utilização de listas

18
Coleções – LinkedList<E>
▸ Exemplo de utilização: public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
for (int val = 0; val < 10; val++) {
list.add(new Integer(val));
}
for (int i = 0; i < list.size(); i++) {
if (list.get(i).intValue() == 5) {
list.remove(i);
}
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}

19
Coleções – Interface Iterator<E>
▸ A Interface Iterator<E> define os métodos essenciais para iterar (percorrer) uma coleção:
▹ hasNext() – Determinar se a coleção tem ou não um elemento seguinte.
▹ next() – Devolve o elemento seguinte da iteração
▹ remove() – Remove o último elemento iterado.

v Exemplo:

Iterator<Person> s1 = persons.iterator();

Ø Neste exemplo s1 vai guardar um objeto de uma classe que desconhecemos Iterator<E>. O objeto é obtido através da
chamada ao método iterator() que existe em todas as coleções (está declarado na interface Collection<E>)
Ø Através de s1 vai ser possível “iterar” (percorrer) a lista de pessoas.
Ø O método remove() vai permitir remover o elemento obtido com next() e é a única forma segura de alterar uma coleção
durante uma iteração.

20
Coleções – Interface Iterator<E>
public static void main(String[] args) {
Definimos stringList a partir da List<String> stringList = new ArrayList<>();
interface List<E>, assim podemos
System.out.println("Com while:");
mais tarde escolher outra stringList = fillList(stringList);
implementação Iterator<String> s1 = stringList.iterator();
▸ Exemplo: while (s1.hasNext()) {
System.out.println(s1.next());
s1.remove();
}

System.out.println("\nCom do-while:");
Vamos preencher a stringList = fillList(stringList);
lista num método Iterator<String> s2 = stringList.iterator();
separado if (s2.hasNext()) {
do {
System.out.println(s2.next());
s2.remove();
} while (s2.hasNext());
}
// Continua…
Iteração dos elementos da lista

21
System.out.println("\nCom for:");
stringList = fillList(stringList);

Coleções – Iterator<String> s3 = stringList.iterator();


for (int i = 0; i < stringList.size(); i++) {
if (s3.hasNext()) {

Interface Iterator<E> System.out.println(s3.next());


s3.remove();
}
} // Fim do main
public static List<String> fillList(List<String> list) {
list.add("IPOO");
list.add("POO");
list.add("PV");

O valor de retorno e o parâmetro list.add("POO");


de entrada são do tipo List<E> list.add("POO");
!!!
return list;
}

22
public static void main(String[] args) {
List<String> listaStrings = new ArrayList<>();

Coleções – Interface
System.out.println("Com while:");

listaStrings = preencherLista(listaStrings);

Iterator<E>
Iterator<String> s1 = listaStrings.iterator();

while (s1.hasNext()) {
System.out.println(s1.next());

s1.remove();

System.out.println("Com do-while:");

listaStrings = preencherLista(listaStrings);
Iterator<String> s2 = listaStrings.iterator();

if (s2.hasNext()) {

do {

System.out.println(s2.next());

s2.remove();
} while (s2.hasNext());

System.out.println("Com for:");

listaStrings = preencherLista(listaStrings);

Iterator<String> s3 = listaStrings.iterator();
for (int i = 0; i < listaStrings.size(); i++) {

if (s3.hasNext()) {

System.out.println(s3.next());

s3.remove();

}
}

} 23
Conjuntos
▸ Coleções

24
Coleções – Interface Set<E>

▸ Set<E> - para conjuntos de elementos sem


duplicações
▹ Representa a noção matemática de
conjunto.
▹ Não existe ordenação de qualquer tipo
sobre os elementos adicionados.

25
Coleções – Classes que implementam
Set<E>
▸ HashSet<E> – armazena os elementos numa hash table.
▹ É a implementação com melhor desempenho mas não garante nada quanto à ordem de
iteração.
▹ HashSet<Person> persons = new HashSet<>();
▹ Declara e cria um HashSet chamado persons para armazenar objetos da classe Person.

▸ Outras implementações de conjuntos (que mantêm a ordem de iteração):


▹ TreeSet<E>
▹ LinkedHashSet<E>
26
Utilização de Sets
▸ A interface Set não associa a cada elemento uma posição dentro da coleção, como acontece na
interface List.

▸ Assim a melhor forma para percorrer os elementos de um conjunto é através do ciclo for aprimorado ou
de um iterator

▸ Tal como com as outras coleções devemos declarar a variável com a interface Set e depois escolher a
implementação desejada (HashSet, TreeSet, LinkedHashSet). Assim minimiza-se o
“Acoplamento de Subclasses”:

Set<String> set = new HashSet<>();


▹ Desta forma podemos optar por diferentes implementações com alterações mínimas.
27
public static void main(String[] args) {

System.out.println("*** HashSet professores");


Set<String> teachers = new HashSet<>();

Coleções - Classe
teachers.add("Ana");
teachers.add("Joao");

HashSet<E>
for (String s : teachers) {

System.out.println(s);
}

Set<String> students = new HashSet<>();


students.add("Joao");

students.add("Luis");

System.out.println("***** HashSet alunos");


for (String s : students) {

System.out.println(s);
}

Set<String> persons = new HashSet<>(teachers);

persons.addAll(students);
System.out.println("******* HashSet pessoas = professores + alunos");

for (String s : persons) {


System.out.println(s);

teachers = new HashSet<>(persons);


teachers.removeAll(students);

System.out.println("********* HashSet professores = pessoas - alunos");


for (String s : teachers) {

System.out.println(s);

}
}
28
Unicidade dos Elementos
▸ Num conjunto para se garantir que os elementos são únicos e não existem duplicados tem
de se redefinir os métodos equals e hashCode
▹ Redefinir apenas um deles não é suficiente
▹ O método boolean equals(Object obj) definido na classe Object devolve um
valor lógico que indica se um objeto é igual a outro passado como argumento.
▹ O método int hashCode() é definido na classe Object e tenta devolver, para
todos os objetos, um valor que o identifique univocamente.

29
Redefinir o método equals
@Override
public boolean equals(Object obj) {
Garante que os objetos são
if (obj == null) { da mesma classe
return false;
}
if (getClass() != obj.getClass()) {
Cast para a classe onde se
return false; está a colocar o método
} (neste caso Person)
final Person other = (Person) obj;
if (!Objects.equals(this.name, other.name)) {
return false;
}
return true;
Verificação da igualdade dos
} dois objetos

30
Redefinir o método equals – versão 2
Auto verificação
@Override
public boolean equals(Object obj) {
if (this == obj) Verificação da validade
return true;
if (obj == null)
Verificação do tipo e cast
return false;
if (getClass() != obj.getClass())
Cast para a classe onde se
return false;
está a colocar o método
Person other = (Person) obj; (neste caso Person)
return Objects.equals(this.name, other.name));
}
Verificação da igualdade dos
dois objetos

31
Redefinir o método equals
▸ Resumo
▹ Os conceitos de identidade e igualdade são diferentes:
▹ Identidade: pode-se verificar se é a mesma referência com ==
▹ Igualdade: podem ser referências diferentes para "o mesmo valor"; verificado com equals
▹ Certificar-se de substituir equals (Object) para que o método redefinido seja sempre chamado.
▹ Incluir uma auto verificação e uma verificação nula para um retorno antecipado em casos extremos
simples.
▹ Usar getClass para permitir que os subtipos tenham a sua própria implementação (mas sem
comparação entre os subtipos) ou usar instanceof e definir equals como final (e os subtipos podem
ser iguais).
▹ Comparar os atributos desejados usando Objects.equals.

32
O “contrato” do método hashCode
▸ Sempre que é chamado no mesmo objeto mais de uma vez durante a execução de uma aplicação
Java, o método hashCode deve retornar consistentemente o mesmo inteiro, desde que nenhuma
informação usada em comparações de igual no objeto seja modificada. Este inteiro não precisa de
permanecer consistente de uma execução da aplicação para outra execução da mesma aplicação.
▸ Se dois objetos são iguais de acordo com o método equals (Object), chamar o método
hashCode em cada um dos dois objetos deve produzir o mesmo resultado inteiro.
▸ Se dois objetos forem diferentes de acordo com o método equals (Object), não é necessário
que a chamada ao método hashCode em cada um dos dois objetos produze resultados inteiros
distintos. No entanto, o programador deve estar ciente de que a produção de resultados inteiros
distintos para objetos desiguais pode melhorar o desempenho das tabelas hash.

33
Redefinir o método hashCode

@Override

public int hashCode() {

return name.hashCode();

} Neste exemplo duas pessoas eram iguais


se tivessem o mesmo nome pelo que
poderíamos usar o hashCode da String
(o name):

34
Redefinir o método hashCode
▸ Resumo
▹ gerar códigos hash é equivalente a “compactar” a igualdade num valor inteiro: objetos iguais devem ter
o mesmo código hash e, por motivos de desempenho, é melhor se o menor número possível de objetos
não iguais partilham o mesmo código hash.
▹ Isso significa que o método hashCode deve sempre ser redefinido se equals é redefinido.
▹ Ao implementar hashCode:
▹ Use os mesmos campos que são usados em equals (ou um subconjunto deles).
▹ É melhor não incluir campos mutáveis.
▹ Considere não chamar hashCode em coleções.
▹ Use um algoritmo comum, a menos que os padrões dos dados os neutralizem.

35
Mapas
▸ Coleções

36
Coleções - Interface Map<K,V>

▸ Map<K,V> - para associações entre dois


elementos, onde um representa a chave (Key) e o
outro o seu valor (Value).
▹ As chaves são únicas (não há chaves repetidas!)
▹ Cada chave refere um único elemento.
▹ Os valores podem ser repetidos desde que
pertençam a chaves diferentes.

37
Coleções – Classes que implementam Map<K,V>
▸ HashMap<K,V> - armazena pares de elementos associados
▹ HashMap<Integer, Person> persons = new HashMap<>();
▹ Cria um objeto HashMap chamado persons em que as chaves são do tipo inteiro e os
valores são objetos da classe Person.
▹ O seu comportamento e desempenho são semelhantes ao conjunto análogo HashSet:
também não temos garantia quanto à ordem de iteração e usa uma hash table na sua
implementação (recorrendo aos métodos equals e hashCode para determinar a unicidade).
▸ Outras implementações de mapas (que mantêm a ordem de iteração):
▹ TreeMap<K,V>
▹ LinkedHashMap<K,V>
38
Utilização de Maps
▸ A interface Map, tal como a interface Set, não associa a cada elemento uma posição dentro da coleção, como
acontece na interface List.
▸ Assim a melhor forma para percorrer os elementos de um mapa é através do ciclo for aprimorado ou de um
iterator.
▸ No entanto como no mapa temos uma associação chave/valor podemos percorrer os seus elementos de várias
formas distintas:
▹ Através do acesso às diversas chaves
▹ Através do acesso exclusivo aos valores
▹ Através do acesso aos pares chave/valor
▸ Devemos manter a estratégia de declararmos as variáveis utilizando a interface Map e definir os seus valores
através de uma implementação concreta (ex.: HashMap)

39
Aceder aos elementos de um Mapa
▸ Supondo que se cria um mapa que associa números (Integer) a nomes de pessoas (String):
▹ Integer – porque as classes genéricas não podem trabalhar com tipos primitivos – int. É
preciso utilizar as classes equivalentes)

public static void main(String[] args) {


Map<Integer, String> namesMap = new HashMap<>();
namesMap.put(13, "Maria");
namesMap.put(43, "Manuel");
namesMap.put(37, "Marco");
namesMap.put(23, "Maria"); //Valor repetido
namesMap.put(43, "Manuel Matos"); // Chave repetida,
// substitui valor anterior
printMap(namesMap); //Mostrar no ecrã }
40
Aceder aos elementos através da chave
▸ A interface Map tem o método keySet() que devolve um Set<K> com todas as
chaves. Assim, recorrendo ao for-each ou a um iterator podemos aceder a todos
os elementos:

public static void printMap(Map<Integer, String> map) {


System.out.println("Pessoas:");
for (Integer i : map.keySet()) {
System.out.println("" + i + " - " + map.get(i));
}
}

41
Aceder aos elementos através dos valores

▸ A interface Map tem o método values() que devolve uma Collection<V> com
todos os valores (sem as chaves). Assim, recorrendo ao for-each ou a um iterator
podemos aceder a todos os elementos:

public static void printMap(Map<Integer, String> map) {


System.out.println("Pessoas:");
for (String name : map.values()) {
System.out.println(name);
}
}

42
Aceder aos elementos através dos pares chave/valor

▸ A interface Map tem o método entrySet() que devolve um Set<Map.Entry<K,V>> com


todas os pares chave/valor (implementados através da classe Map.Entry). Assim, recorrendo ao
for-each ou a um iterator podemos aceder a todos os elementos:

public static void printMap(Map<Integer, String> map) {


System.out.println("Pessoas:");
for (Map.Entry pair : map.entrySet()) {
System.out.println("" + pair.getKey() + " - " + pair.getValue());
}
}

43
Exemplo Escola,
Algoritmos
▸ Coleções

44
Exemplo Escola
▸ Criar um sistema para registar as notas de alunos.
▸ Serão necessárias as seguintes classes:
▹ Student – informação de um aluno (número e nome)
▹ O número é único, identifica o aluno e deve ser gerado
automaticamente
▹ SchoolClass - informação da turma
▹ Grade - onde se associam os alunos às notas

45
Exemplo Escola
▸ Classe Student public class Student {

private static int nextNumber = 1;


private int number;
private String name;

public Student(String name) {


this.number = Student.nextNumber++;
this.name = name;
}
// Continua…

46
Exemplo Escola
public int getNumber() {
▸ Classe Student – métodos seletores return number;
}
e modificadores
public String getName() {
return name;
}

public void setName(String name) {


this.name = name;
}

@Override
public String toString() {
return number + " - " + name;
}

47
Exemplo Escola
@Override
public int hashCode() {
Integer number = new Integer(this.number);
▸ Classe Student – return number.hashCode();
}
métodos equals e hashCode
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
Usa o HashCode da classe Integer }
if (getClass() != obj.getClass()) {
return false;
}
O número é usado
para distinguir dois return this.number == ((Student) obj).number;
alunos }

48
Exemplo Escola
▸ Classe SchoolClass

public class SchoolClass {

private Map<Integer, Student> students; Para ser mais simples de


aceder aos alunos através
private String name; do seu número usamos
uma associação entre o
public SchoolClass(String name) { número (chave) e o aluno
this.name = name; (valor)
students = new HashMap<>();
}
Cuidado!!!
// Continua… Temos de manter o número
de aluno da chave idêntico ao
que está guardado no objeto
aluno associado

49
Exemplo Escola
public String getName() {
▸ Classe SchoolClass – return name;
Métodos (1/2) }

public void setName(String name) {


this.name = name;
}

public void add(Student student) {


students.put(student.getNumber(), student);
}

public Student get(int number) {


return students.get(number);
}

50
Exemplo Escola
public Student remove(int number) {
return students.remove(number);
▸ Classe SchoolClass – }

Métodos (2/2) public boolean isEnrolled(int number) {


return students.containsKey(number);
}

@Override
public String toString() {
String list = name + ":";
for (Student student : students.values()) {
list += "\n" + student;
}
return list;
}

51
Exemplo Escola
▸ Classe Grade

public class Grade extends HashMap<Student, Integer> {

@Override
public String toString() {
Neste caso consideramos
String grades = "Notas:"; a avaliação como uma
for (Student student : keySet ()) { associação entre aluno e
grades += "\n" + student + ": " + get(student); nota
}
return grades;
}

}
Cuidado!!!
Devemos ter em atenção se não
será necessário redefinir algum dos
métodos herdados.

52
▸ Programa Principal

Exemplo Escola E se quiséssemos


ordenar os alunos
public static void main(String[] args) { pelo nome?
SchoolClass schoolClass = new SchoolClass("Turma da LEI");
schoolClass.add(new Student("Rita")); //Fica com nº 1
schoolClass.add(new Student("Manuel")); //Fica com nº 2
schoolClass.add(new Student("Anibal")); //Fica com nº 3
schoolClass.add(new Student("Maria")); //Fica com nº 4
System.out.println(schoolClass);
Grade grades = new Grade();
grades.put(schoolClass.get(1), 12); //Rita
grades.put(schoolClass.get(2), 14); //Manuel
grades.put(schoolClass.get(3), 8); //Aníbal
grades.put(schoolClass.get(4), 17); //Maria
grades.put(schoolClass.get(3), 11); //Recurso do Aníbal
System.out.println(grades);
}

53
Coleções e a Java Collections Framework
▸ Java Collections Framework (JCF):
▹ É uma arquitetura unificada que inclui interfaces, classes (abstratas e concretas) e algoritmos
(implementados por métodos).

v Como foi dito anteriormente a JCF contém as interfaces que especificam as coleções e classes
(concretas) que implementam essas coleções.
v Além disso ainda tem classes abstratas que implementam parcialmente coleções e algoritmos.
v Os algoritmos permitem várias operações sobre as coleções como por exemplo: a ordenação, a inversão
da ordem dos elementos, a mistura aleatória dos elementos, etc.

54
Java Collections Framework – Algoritmos
▸ Os algoritmos da JCF são fornecidos como métodos estáticos da classe Collections e a maioria aplica-se especificamente a listas,
alguns deles são:
▹sort – ordena uma lista por um critério
▹shuffle – baralha os elementos da lista aleatoriamente
▹reverse – reverte a ordem dos elementos na lista
▹rotate – roda todos os elementos da lista numa distância especificada
▹swap – troca os elementos em posições especificadas de uma lista
▹replaceAll – troca todas as ocorrências de um valor por um outro valor especificado
▹fill – atribui a todos os elementos da lista um valor especificado
▹copy – copia uma lista para outra
▹binarySearch – procura um elemento numa lista com o algoritmo de procura binária
▸ Exemplo de evocação de um algoritmo:
▹ Collections.rotate(names, 4);
▹ Passa os 4 últimos elementos de names para o princípio da lista names pela mesma ordem em que estavam no final da lista.
55
Exemplo Escola - Ordenação dos elementos
de uma coleção
▸ Poderia ser interessante apresentar as notas dos alunos ordenando-os alfabeticamente pelo seu nome
▸ Como foi referido, existem diversos métodos, de classe, que permitem a manipulação dos elementos de
uma coleção. Para ordenar será necessário utilizar o método Collections.sort
▹ A ordenação é feita comparando os elementos da coleção: dados dois elementos é preciso saber se um é "menor",
"igual" ou "maior" que o outro
▹ Existem então três valores possíveis nessa comparação.

▹ A forma mais simples de a fazer é recorrer a um método que devolva um valor negativo em caso de "menor", um
valor positivo em caso de "maior" e zero em caso de igualdade.

56
Interface Comparable<T>
▸ A necessidade de comparar elementos é tão importante e comum que o Java disponibiliza a interface:

public interface Comparable<T> {

int compareTo(T o);

▸ A interface Comparable apenas obriga à implementação do método compareTo que recebe um elemento do
mesmo tipo e devolve um valor inteiro positivo, negativo ou zero, consoante o elemento a comparar seja maior, menor
ou igual ao elemento fornecido.

▸ Todas as classes que pretendem ordenar os seus objetos devem implementar esta interface, indicando no método
compareTo o algoritmo de comparação.
57
Exemplo Escola - Ordenação pelo nome dos
Alunos
▸ Para os alunos poderem ser ordenados por nome é preciso que a classe Student implemente a interface
Comparable< Student >:
public class Student implements Comparable<Student> {
...
}

▸ Obriga a que seja implementado o método compareTo


▹ Neste caso não é verificado se o student é diferente de null e é utilizada a chamada ao método
compareTo da classe String, que implementa a interface Comparable<String>
@Override
public int compareTo(Student student) {
return name.compareTo(student.name);
}
58
Exemplo Escola - Ordenação na apresentação das
notas
@Override
▸ Para produzir uma pauta public String toString() {
List<Student> students = new ArrayList<>(keySet());
ordenada (será necessário
Collections.sort(students);
modificar o toString de
String grades = "Notas:";
Grade) é preciso recolher os for (Student student : students) {
alunos numa lista e ordená-la grades += "\n" + student + ": " + get(student);
}
(por nome), utilizando-a, de
return grades;
seguida, para obtenção das
}
notas:

59
Exemplo Escola - Pauta Ordenada

▸ As notas são apresentadas com os

alunos ordenados por nome:

E se
quiséssemos
ordenar agora os
alunos pelo
número ou por
outro valor?

60
Interface Comparator<T>
▸ A solução que utiliza a interface Comparable leva a que a ordenação dos objetos duma classe seja feita
apenas da forma que é definida através do método compareTo
▸ Outra solução é utilizar a interface Comparator:
public interface Comparator<T> {
int compare(T o1, T o2);
}
▸ O método compare funciona da mesma forma que o método compareTo com a diferença que recebe como
argumento dois objetos da mesma classe
▹ Neste caso pode-se criar uma classe separada para cada tipo de comparação que se quer fazer e depois será possível
usá-la no algoritmo de ordenação da classe Collections.

61
Exemplo Escola - Ordenação pelo número dos Alunos

▸ Para os alunos poderem ser ordenados pelo número é preciso criar uma classe que implementa a interface
Comparator<Student> fornecendo o método de comparação compare que compara dois alunos pelos seus
números.

public class StudentNumberComparator implements Comparator<Student> {

@Override
public int compare(Student student1, Student student2) {
return student1.getNumber() - student2.getNumber();
}
}

62
Exemplo Escola - Ordenação na apresentação das notas
▸ Para produzir uma pauta ordenada pelo número de alunos modificando o toString de Grade é preciso depois de
recolher os alunos numa lista e usar a ordenação com a classe StudentNumberComparator criada:

@Override
public String toString() {
List<Student> students = new ArrayList<>(keySet());
Collections.sort(students, new StudentNumberComparator());

String grades = "Notas:";


for (Student student : students) {
grades += "\n" + student + ": " + get(student);
}
return grades;
} Neste caso o método sort recebe o
objeto comparador como segundo
parâmetro
63
Exemplo Escola - Pauta Ordenada

▸ As notas são apresentadas com os alunos

ordenados pelo número:

64
Resumindo
▸ Coleção – agregado de elementos de um mesmo tipo

▸ JCF – arquitetura que inclui: Interfaces, Classes Abstratas e Concretas, Algoritmos


▹ Interfaces genéricas – Collection<E> - a partir das quais se implementam classes de coleção específicas para guardar
objetos de um dado tipo <E>
▹ Os algoritmos da classe Collections manipulam listas com grande eficiência.

▸ Listas
▹ Classe ArrayList<E> e LinkedList<E>

▸ Iteração e Alterações em Coleções


▹ Ciclo aprimorado for (type safe)
▹ Não modificar a coleção durante a iteração … a não ser que se use o …
▹ Iterator<E> – a única forma segura de iterar e alterar uma coleção em simultâneo
65
Resumindo
▸ Conjuntos
▹ HashSet – implementação baseada em hash table
▹ LinkedHashSet e TreeSet – implementações que garantem a ordem de iteração
▸ Unicidade de objetos através do uso dos métodos equals e hashCode
▸ Mapas
▹ HashMap – implementação baseada em hash table
▹ LinkedHashMap e TreeMap – implementações que garantem a ordem de iteração
▹ Utilizar for-each ou iterator para percorrer os elementos, através das:
▹ Chaves – keySet()
▹ Valores – values()
▹ Pares chave/valor – entrySet()

66
Resumindo
▸ A interface Comparable<T>, através do seu método compareTo, permite a comparação
de objetos (devolvendo < 0, > 0 ou = 0, consoante os valores sejam menores, maiores ou iguais)

▸ A interface Comparator<T>, através do seu método compare, pode ser utilizada por uma
classe onde se define a comparação de dois objetos por uma determinada forma.

▸ Algoritmos

▹ O uso do método Collection.sort permite a ordenação dos elementos

67
Programação Orientada por Objetos

Genéricos

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Exemplo de Listas
▸ Exemplo com Listas de Objetos

▸ Listas com Genéricos


▸ Genéricos: Tópicos Avançados

2
Exemplo de listas
▸ Genéricos

3
Exemplo – Listas
▸ Requisitos :
▹ Pretende-se criar um programa que irá guardar
informação de nomes, de pessoas e de outro tipo de
listas.
▹ Deverá ser possível como é habitual em listas
adicionar, alterar, remover e listar os elementos
das listas.
▹ Como restrição não será possível utilizar as
classes de coleção do Java.

4
Exemplo – Listas
▸ Classe ListOfNames

Tamanho do array
public class ListOfNames {

private static final int DEFAULT_SIZE = 5;


private String[] values;
private int totalValues; Nomes

public ListOfNames() { Número de elementos no array


values = new String[DEFAULT_SIZE];
totalValues = 0;
}

// continua…
Vamos guardar os
nomes num array.

5
Exemplo – Listas
▸ Classe ListOfNames – métodos add e remove
O array cresce
public void add(String element) { automaticamente quando
if (totalValues == values.length) { atinge o limite
String[] newValues = new String[values.length * 2];
for (int i = 0; i < values.length; i++) {
newValues[i] = values[i];
}
values = newValues;
}
values[totalValues++] = element;
}

public boolean remove(int position) {


if ((position >= 0) && (position < totalValues)) {
for (int i = position; i < totalValues - 1; i++) {
values[i] = values[i + 1];
}
totalValues--;
return true; Adicionar e remover
} else { nomes do array.
return false;
}
}
6
Exemplo – Listas
▸ Classe ListOfNames – métodos get, set e size
public String get(int position) {
if ((position >= 0) && (position < totalValues)) {
return values[position];
} else {
return null;
}
} Alterar um nome

public boolean set(int position, String element) {


if ((position >= 0) && (position < totalValues)) {
values[position] = element;
return true;
} else {
return false;
}
} Obter e alterar
nomes do array.
public int size() {
return totalValues;
}
Total de nomes no array
7
Exemplo – Listas
▸ Classe ListOfNames – métodos capacity e toString

public int capacity() {


return values.length;
Capacidade do array
}

@Override
public String toString() {
String result = "[";
boolean first = true;
for (int i = 0; i < totalValues; i++) {
if (first) {
first = false; Listar os nomes
} else { do array.
result += ", ";
}
result += values[i];
}
Escreve os nomes separados
result += "]";
return result; por vírgulas
}

8
Exemplo – Listas
▸ Classe ListOfNames – utilização
public static void testListOfNames() {
ListOfNames names = new ListOfNames();
System.out.println("No início: capacity=" + names.capacity());
names.add("Bruno");
names.add("Fausto");
names.add("José");
names.add("Rui");
names.add("Patricia");
names.add("Joaquim");
System.out.println("Depois de inseridos os elementos: capacity=" + names.capacity());
System.out.println("names=" + names);
System.out.println("size=" + names.size());
System.out.println("names[4]=" + names.get(4));
System.out.println("names[10]=" + names.get(10));
names.remove(4);
System.out.println("Depois de remove(4): names=" + names);
System.out.println("size=" + names.size());
names.set(4, "Silva");
System.out.println("Depois de set(4): names=" + names);
}

9
Exemplo – Listas
NOMES:
No início: capacity=5
Depois de inseridos os elementos: capacity=10
nomes=[Bruno, Fausto, José, Rui, Patricia, Joaquim]
size=6
nomes[4]=Patricia

▸ Classe ListOfNames – utilização nomes[10]=null


Depois de remove(4): nomes=[Bruno, Fausto, José, Rui,
Joaquim]
size=5
public static void testListOfNames() { Depois de set(4): nomes=[Bruno, Fausto, José, Rui, Silva]
ListOfNames names = new ListOfNames();
System.out.println("No início: capacity=" + names.capacity());
names.add("Bruno");
names.add("Fausto");
names.add("José");
names.add("Rui");
names.add("Patricia");
names.add("Joaquim");
System.out.println("Depois de inseridos os elementos: capacity=" + names.capacity());
System.out.println("names=" + names);
System.out.println("size=" + names.size());
System.out.println("names[4]=" + names.get(4));
System.out.println("names[10]=" + names.get(10));
names.remove(4);
System.out.println("Depois de remove(4): names=“
+ names);
System.out.println("size=" + names.size());
names.set(4, "Silva");
System.out.println("Depois de set(4): names="
+ names);
}

10
public class Person {

Exemplo – Listas private int age;


private String name;

public Person(String name, int age) {


this.name = name;
this.age = age;
▸ Classe Person }

public int getAge() {


return age;
}

public void setAge(int age) {


this.age = age;
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}

@Override
public String toString() {
return name + " (" + age + " anos)";
}
}
11
Exemplo – Listas
▸ Classe ListOfPerson
Tamanho do array
public class ListOfPerson {

private static final int DEFAULT_SIZE = 5;


private Person[] values;
private int totalValues; Pessoas

public ListOfPerson() { Número de elementos no array


values = new Person[DEFAULT_SIZE];
totalValues = 0;
}

// continua…
Vamos guardar as
pessoas num array
como foi feito com os
nomes

12
Exemplo – Listas
▸ Classe ListOfPerson – métodos add e remove
public void add(Person element) {
if (totalValues == values.length) {
Person[] newValues = new Person[values.length * 2];
for (int i = 0; i < values.length; i++) {
newValues[i] = values[i];
}
values = newValues;
}
values[totalValues++] = element;
}

public boolean remove(int position) {


if ((position >= 0) && (position < totalValues)) {
for (int i = position; i < totalValues - 1; i++) {
values[i] = values[i + 1];
}
totalValues--;
return true; Adicionar e remover
} else { pessoas do array.
return false;
}
}

13
Exemplo – Listas
▸ Classe ListOfPerson – métodos get, set e size
public Person get(int position) {
if ((position >= 0) && (position < totalValues)) {
return values[position];
} else {
return null;
}
}

public boolean set(int position, Person element) {


if ((position >= 0) && (position < totalValues)) {
values[position] = element;
return true;
} else {
return false;
} Obter e alterar
} pessoas do array.
public int size() {
return totalValues;
}
14
Exemplo – Listas
▸ Classe ListOfPerson – métodos capacity e toString
public int capacity() {
return values.length;
}

@Override
public String toString() {
String result = "[";
boolean first = true;
for (int i = 0; i < totalValues; i++) {
if (first) {
first = false;
} else {
result += ", ";
}
result += values[i]; Listar as pessoas
} do array.
result += "]";
return result;
}

15
Exemplo – Listas
▸ Classe ListOfPerson – utilização
public static void testListOfPerson() {
ListOfPerson persons = new ListOfPerson();
System.out.println("No início: capacity=" + persons.capacity());
persons.add(new Person("Maria", 28));
persons.add(new Person("Manuel", 34));
persons.add(new Person("Marta", 45));
persons.add(new Person("Mauro", 53));
persons.add(new Person("Miguel", 19));
persons.add(new Person("Margarida", 26));
System.out.println("Depois de inseridos os elementos: capacity=" + persons.capacity());
System.out.println("persons=" + persons);
System.out.println("size=" + persons.size());
System.out.println("persons[4]=" + persons.get(4));
System.out.println("persons[10]=" + persons.get(10));
persons.remove(4);
System.out.println("Depois de remove(4): persons=" + persons);
System.out.println("size=" + persons.size());
persons.set(4, new Person("Matos", 47));
System.out.println("Depois de set(4): persons=" + persons);
}
16
PESSOAS:
No início: capacity=5
Depois de inseridos os elementos: capacity=10

Exemplo – Listas
pessoas=[Maria (28 anos), Manuel (34 anos), Marta (45 anos), Mauro (53 anos),
Miguel (19 anos), Margarida (26 anos)]
size=6
pessoas[4]=Miguel (19 anos)
pessoas[10]=null
▸ Classe ListOfPerson – utilização Depois de remove(4): pessoas=[Maria (28 anos), Manuel (34 anos), Marta (45
anos), Mauro (53 anos), Margarida (26 anos)]
size=5
Depois de set(4): pessoas=[Maria (28 anos), Manuel (34 anos), Marta (45 anos),
Mauro (53 anos), Matos (47 anos)]
public static void testListOfPerson() {
ListOfPerson persons = new ListOfPerson();
System.out.println("No início: capacity=" + persons.capacity());
persons.add(new Person("Maria", 28));
persons.add(new Person("Manuel", 34));
persons.add(new Person("Marta", 45));
persons.add(new Person("Mauro", 53));
persons.add(new Person("Miguel", 19));
persons.add(new Person("Margarida", 26));
System.out.println("Depois de inseridos os elementos: capacity=" + persons.capacity());
System.out.println("persons=" + persons);
System.out.println("size=" + persons.size());
System.out.println("persons[4]=" + persons.get(4));
System.out.println("persons[10]=" + persons.get(10));
persons.remove(4);
System.out.println("Depois de remove(4): persons=" + persons);
System.out.println("size=" + persons.size());
persons.set(4, new Person("Matos", 47));
System.out.println("Depois de set(4): persons=" + persons);
}
17
Exemplo Listas
▸ Análise das soluções
▹ Quando se consegue saber à priori o número de elementos que se
pretende guardar, a utilização de um array é uma solução eficiente
▹ Quando não se sabe o número de elementos é preferível utilizar uma das
classes de coleção do Java.
▹ Mas as duas soluções são muito parecidas…
▹ Temos muita duplicação de código!
▹ Uma solução para o problema da duplicação de código pode ser criar
uma única lista de Object

18
Exemplo com
listas de Objetos
▸ Genéricos

19
Exemplo – Listas

▸ Requisitos :
▹ Pretende-se criar um programa que irá guardar informação
de nomes, de pessoas e de outro tipo de listas.
▹ Deverá ser possível como é habitual em listas
adicionar, alterar, remover e listar os elementos das
listas.
▹ Como restrição não será possível utilizar as classes de
coleção do Java.

20
Exemplo – Listas
▸ Classe ListOfObject

public class ListOfObject {

private static final int DEFAULT_SIZE = 5;


private Object[] values;
private int totalValues; Qualquer tipo de objeto

public ListOfObject() {
values = new Object[DEFAULT_SIZE];
totalValues = 0;
}

// continua…
Vamos guardar os
objetos num array

21
Exemplo – Listas
▸ Classe ListOfObject – métodos add e remove
public void add(Object element) {
if (totalValues == values.length) {
Object[] newValues = new Object[values.length * 2];
for (int i = 0; i < values.length; i++) {
newValues[i] = values[i];
}
values = newValues;
}
values[totalValues++] = element;
}

public boolean remove(int position) {


if ((position >= 0) && (position < totalValues)) {
for (int i = position; i < totalValues - 1; i++) {
values[i] = values[i + 1];
}
totalValues--;
return true;
} else {
return false;
}
} 22
Exemplo – Listas
▸ Classe ListOfObject – métodos get, set e size
public Object get(int position) {
if ((position >= 0) && (position < totalValues)) {
return values[position];
} else {
return null;
}
}

public boolean set(int position, Object element) {


if ((position >= 0) && (position < totalValues)) {
values[position] = element;
return true;
} else {
return false;
}
}

public int size() {


return totalValues;
}
23
Exemplo – Listas
▸ Classe ListOfObject – métodos capacity e toString

public int capacity() {


return values.length;
}

@Override
public String toString() {
String result = "[";
boolean first = true;
for (int i = 0; i < totalValues; i++) {
if (first) {
first = false;
} else {
result += ", ";
}
result += values[i];
}
result += "]";
return result;
}
24
Exemplo – Listas
▸ Classe ListOfObject – utilização com uma lista de Pessoas
public static void testListOfObject() {
ListOfObject objects = new ListOfObject();
System.out.println("No início: capacity=" + objects.capacity());
objects.add("Bruno");
objects.add("Fausto");
objects.add("José");
objects.add(new Person("Mauro", 53));
objects.add(new Person("Miguel", 19));
objects.add(new Person("Margarida", 26));
System.out.println("Depois de inseridos os elementos: capacity=" + objects.capacity());
System.out.println("objects=" + objects);
System.out.println("size=" + objects.size());
System.out.println("objects[4]=" + objects.get(4));
O código neste caso
System.out.println("objects[10]=" + objects.get(10));
objects.remove(4);
é semelhante ao
System.out.println("Depois de remove(4): objects=" + objects); anterior para a lista
System.out.println("size=" + objects.size()); de pessoas. Mas…
objects.set(4, new Person("Matos", 47));
System.out.println("Depois de set(4): objects=" + objects);
}
25
Exemplo Listas
▸ Análise da solução ListOfObject
▹ Embora a solução com a classe ListOfObject seja semelhante existem alguns problemas.
▹ A colocação de elementos na lista faz-se como anteriormente tirando partido do principio da substituição
▹ objets.add(new Person("Margarida", 26));
▹ O método recebe Object como argumento e estamos a passar um objeto da classe Person
▹ Quando se obtêm elementos da lista a situação é diferente
▹ Person person = (Person)objets.get(4);
▹ É necessário fazer um cast porque o método retorna Object
▹ Outro problema é que o tipo de elementos guardado na lista não é verificado e podemos misturar objetos
de diferentes classes
▹ objets.add("Margarida");
▹ Neste caso adicionámos uma String
26
Exemplo Listas
com Genéricos
▸ Genéricos

27
Exemplo – Listas

▸ Requisitos :
▹ Pretende-se criar um programa que irá guardar informação de
nomes, de pessoas e de outro tipo de listas.
▹ Deverá ser possível como é habitual em listas adicionar,
alterar, remover e listar os elementos das listas.
▹ Como restrição não será possível utilizar as classes de
coleção do Java.

28
Exemplo – Listas
▸ Classe ListaOfNames versus ListOfPerson
public class ListOfName { public class ListOfPerson {

private static final int DEFAULT_SIZE = 5; private static final int DEFAULT_SIZE = 5;
private String[] values; private Person[] values;
private int totalValues; private int totalValues;

public ListOfName() { public ListOfPerson() {


values = new String[DEFAULT_SIZE]; values = new Person[DEFAULT_SIZE];
totalValues = 0; Mudam apenas totalValues = 0;
os tipos de
} }
dados
public void add(String element) { public void add(Person element) {
if (totalValues == values.length) { if (totalValues == values.length) {
String[] newValues = Person[] newValues =
new String[values.length * 2]; new Person[values.length * 2];
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
newValues[i] = values[i]; newValues[i] = values[i];
} }
values = newValues; values = newValues;
} }
values[totalValues++] = element; values[totalValues++] = element;
} }

// continua… // continua…

29
▸ Classe ListaOfNames versus ListOfPerson

Exemplo – Listas // continuação ListOfPerson


// continuação ListOfNames public boolean remove(int position) {
public boolean remove(int position) { if ((position >= 0) && (position < totalValues)) {
if ((position >= 0) && (position < totalValues)) { for (int i=position; i<totalValues-1; i++) {
for (int i = position; i < totalValues - 1; i++) { values[i] = values[i + 1];
values[i] = values[i + 1];
}
} totalValues--;
totalValues--;
return true;
return true; } else {
Mudam apenas
} else { os tipos de return false;
return false; dados }
} }
}
public Person get(int position) {
public String get(int position) { if ((position >= 0) && (position < totalValues)) {
if ((position >= 0) && (position < totalValues)) {
return values[position];
return values[position]; } else {
} else {
return null;
return null; }
} }
}
public boolean set(int position, Person element) {
public boolean set(int position, String element) { if ((position >= 0) && (position < totalValues)) {
if ((position >= 0) && (position < totalValues)) { values[position] = element;
values[position] = element; return true;
return true;
} else {
} else { return false;
return false;
}
} }
} // continua…
// continua…
30
Exemplo – Listas
▸ Classe ListaOfNames versus ListOfPerson
// continuação ListOfNames // continuação ListOfPerson

public int size() { public int size() {


return totalValues; return totalValues;
} Iguais neste }
caso!
public int capacity() { public int capacity() {
return values.length; return values.length;
} }

@Override @Override
public String toString() { public String toString() {
String result = "["; String result = "[";
boolean first = true; boolean first = true;
for (int i = 0; i < totalValues; i++) { for (int i = 0; i < totalValues; i++) {
if (first) { if (first) {
first = false; first = false;
} else {
} else { E se result += ", ";
result += ", ";
}
pudéssemos }
result += values[i]; fornecer o tipo result += values[i];
}
} de dados dentro result += "]";
result += "]";
return result; duma variável? return result;
} }
}
31
Tipos Genéricos
▸ Tipo Genérico (Generic) ou Tipo Parametrizado (type parameters).
▹ Na definição de uma classe, ou de um método, é possível indicar (entre < >) um parâmetro que representa um tipo
de dados.
▹ Este parâmetro será utilizado nos locais onde se colocaria o tipo de dados (indicação do tipo dos atributos,
na lista de parâmetros dos métodos, na declaração de variáveis)

public class List<E> {


... Parâmetro E
}
Parâmetro T

public static <T> void information(T t) {


System.out.println("T: " + t.getClass().getName());
}
Utilização do parâmetro T
como um tipo de dados
32
Tipos Genéricos
▸ Tipo Genérico (Generic) convenção de nomes:
▹ Por convenção, os type parameter são representados normalmente apenas por
uma letra, que indica o que o tipo representa:
▹ E - Element (utilizado regularmente nas coleções do Java)
▹ K - Key
▹ N - Number
▹ T - Type
▹ V - Value
▹ S, U, V etc. – 2º, 3º, 4º tipos
33
Exemplo – Listas
▸ Classe List Genérica O tipo E é fornecido
quando se criam os objetos
public class List<E> {

private static final int DEFAULT_SIZE = 5;


private E[] values;
private int totalValues;

public List() {
values = (E[]) new Object[DEFAULT_SIZE];
totalValues = 0;
} Não é possível fazer
// continua… new E[…] em Java

n Em Java não é possível criar arrays de tipos genéricos. A solução é criar


um array de Object e depois fazer o cast para o tipo genérico

34
Exemplo – Listas
▸ Classe List Genérica – métodos add e remove
public void add(E element) {
if (totalValues == values.length) {
Object[] newValues = new Object[values.length * 2];
for (int i = 0; i < values.length; i++) {
newValues[i] = values[i];
}
values = (E[]) newValues;
} Adicionar e remover
values[totalValues++] = element; elementos do tipo E do
} array.

public boolean remove(int position) {


if ((position >= 0) && (position < totalValues)) {
for (int i = position; i < totalValues - 1; i++) {
values[i] = values[i + 1];
}
totalValues--;
return true;
} else {
return false;
}
}
35
Exemplo – Listas
▸ Classe List Genérica – métodos get, set e size
public E get(int position) {
if ((position >= 0) && (position < totalValues)) {
return values[position];
} else {
return null;
}
}

public boolean set(int position, E element) {


if ((position >= 0) && (position < totalValues)) {
values[position] = element;
return true; Obter e alterar
} else { elementos do tipo E do
return false; array
}
}

public int size() {


return totalValues;
}
36
Exemplo – Listas
▸ Classe List Genérica – métodos capacity e toString

public int capacity() {


return values.length;
}

@Override
public String toString() {
String result = "[";
boolean first = true;
for (int i = 0; i < totalValues; i++) {
if (first) {
first = false;
} else {
result += ", ";
}
result += values[i];
}
result += "]";
return result;
}

37
Exemplo – Listas
▸ Classe List <E> – utilização
▹ No momento da utilização da classe indica-se o tipo pretendido:
▹ List<String> names = new List<String>();
▹ List<Person> persons = new List<Person>();
▹ A partir da Java SE 7 é possível omitir a indicação do tipo, sempre que o compilador consiga
determiná-lo. Utilizando-se a chamada notação diamante <>:
▹ List<String> names = new List<>();
▹ List<Person> persons = new List<>();
▹ Na utilização dos métodos da classe não é necessário fazer qualquer modificação. Continua-se a
poder utilizar elementos de classes derivadas:
▹ Exemplo com uma classe Worker derivada de Person:
persons.set(4, new Worker("Matos", 47));
38
Métodos genéricos - Utilização
▸ A chamada a um método genérico deve indicar o tipo de dados a utilizar:
public class Generics {
...
public static <T> void information(T t) {
System.out.println("T: " + t.getClass().getName());
}
...
}

public static void main(String[] args) {


List<String> names = new List<>();
Generics.<List<String>>information(names);
}

▸ Podemos omitir caso o compilador consiga determinar o tipo:


Generics.information(names);
39
Genéricos: tópicos
avançados
▸ Genéricos

40
Múltiplos Tipos Parametrizados
▸ Podem ser indicados mais do que um type parameter:

public class Association<K, V> {


private K key;

private V value;

public Association(K key, V value) { Representa uma


associação entre
this.key = key; elementos chave (Key)
this.value = value; e valor (Value)
}

public K getKey() { return key; }


public void setKey(K key) { this.key = key; }

public V getValue() { return value; }

public void setValue(V value) { this.value = value; }

}
41
Limitar o Tipo Parametrizado
▸ É possível restringir o type parameter a um determinado tipo ou seus descendentes (através do uso de extends):
public class Association<K extends Number, V extends Person> {
private K key;
private V value;
...
}

▸ Desta forma podemos utilizar os métodos conhecidos do tipo:


@Override
public String toString() {
return key + "-" + value.getName();
}

Nota: Number tem como descendentes AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte,
Double, Float, Integer, Long, Short ou outros que sejam definidos
42
Limitar o Tipo Parametrizado
▸ A restrição pode ser feita de forma múltipla:
public class A {
...
}
public interface B {
...
}
public interface C {
...
}

public class D <T extends A & B & C> {


...
}

▸ A classe deverá ser indicada em primeiro lugar


43
Erro comum na perceção da Herança
▸ Apesar de se poder atribuir a uma lista de pessoas elementos que são de classes filhas
(ex.: Worker). Não existe nenhuma relação entre List<Person> e List<Worker>,
não sendo permitido a sua "mistura":

44
Erro comum na perceção da Herança
Person
▸ Worker herda de Person:
Worker

▸ Mas List<Worker> não herda de List<Person>: List<Person>

List<Worker>

▸ Ambas List<Worker> e List<Person> herdam de Object:


Object

List<Person> List<Worker>
45
Uso de ? (wild-card )
▸ O problema fica resolvido através da indicação de que o tipo de elementos da lista
pode ser qualquer tipo (indicado através de ?) que herde de Person:
//List<Person> error = workers;
List<? extends Person> noError = workers;

▸ ? extends Person indica qualquer tipo de herde de Person (inclusive). Desta forma
indicamos não um tipo de lista mas sim uma "família de tipos de listas" que estão
relacionados pela relação de herança dos seus elementos.
▸ Também é possível a notação ? super T. Neste caso, seriam aceites elementos que
fossem superclasses do tipo T (inclusive).

46
Limitações ao uso de Tipos Parametrizados
▸ Não é possível fazer cast com um type parameter:
example = (E)any;

▸ Não é possível fazer instanceof com um type parameter:


if (example instanceof E)

▸ Não é possível criar arrays de Tipos Parametrizados:


List<Integer>[] arrayLists = new List<Integer>[2];

▸ Tipos Parametrizados não podem ser criados para fazer throw ou catch.
▸ Não pode ser feito polimorfismo de métodos que diferem apenas em Tipos Parametrizados:
public class Wrong {
public void print(List<String> listString) { }
public void print(List<Integer> listInteger) { }
}

47
Resumindo
▸ O uso de Tipos Parametrizados permite definir classes e/ou métodos genéricos que envolvem tipos que
apenas serão concretizados no momento da utilização

▸ Os Tipos parametrizados são, normalmente, representados por uma letra que indica o que o tipo
representa.

▸ É possível omitir o tipo envolvido na utilização de métodos desde que o compilador o consiga determinar
(poderá ser necessário usar a notação <>)

▸ Podem ser utilizados múltiplos tipos parametrizados e podemos limitar a gama de tipos a utilizar
▸ Pode ser necessário recorrer ao uso de ? para indicar relações entre tipos parametrizáveis.
▸ Existem algumas situações em que não é possível usar tipos parametrizáveis.

48
Programação Orientada por Objetos

As coleções
HashSet,
HashMap
Prof. Cédric Grueau
Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Introdução às coleções
▹ As coleções HashSet e HashMap.

2
Exemplo – Sistema de Apoio Técnico
▸ Sistema de apoio técnico:
▹ Criado para substituir o apoio técnico dado aos clientes da
empresa DodgySoft.
▹ O antigo sistema permitia através do telefone, o
esclarecimento de dúvidas e a resolução de problemas
relacionados com os produtos da empresa.
▹ Dadas as dificuldades pelas quais passa a empresa foi
decidido acabar com o departamento de apoio técnico e
construir um sistema que imitasse as respostas dos técnicos
funcionando online e dando a sensação que o apoio técnico
continuava a ser prestado.
3
Exemplo – Sistema de Apoio Técnico
▸ Utilização Welcome to the DodgySoft Technical Support System.

(demonstração) Please tell us about your problem.


We will assist you with any problem you might have.
Please type 'bye' to exit our system.
> help
That sounds interesting. Tell me more...
> My computer has a problem
That sounds interesting. Tell me more...
> nothing else
That sounds interesting. Tell me more...
> pleaseeeeeeeeeee
That sounds interesting. Tell me more...
> bye
Nice talking to you. Bye...

4
Exemplo – Sistema de Apoio Técnico

5
Exemplo – Sistema de Apoio Técnico

▸ Classes do tech support


▹ InputReader – Usada para ler os dados do
utilizador, representa o leitor de dados.
▹ Responder – usada para gerar a resposta a ser
dada ao utilizador.
▹ SupportSystem – usada para gerir o sistema e
interagir com o utilizador, lê o texto do utilizador
e mostra a resposta.

6
Exemplo – Sistema de Apoio Técnico
public class InputReader {

▸ Classe InputReader private Scanner reader;

public InputReader() {
reader = new Scanner(System.in);
}

public String getInput() {


System.out.print("> "); // print prompt
String inputLine = reader.nextLine();

return inputLine;
}
}

7
Exemplo – Sistema de Apoio Técnico
public class Responder {

public Responder() {

▸ Classe Responder }

public String generateResponse() {


return "That sounds interesting. Tell me more...";
}

8
Exemplo – Sistema de Apoio Técnico
public class SupportSystem {
▸ Classe SupportSystem
private InputReader reader;
private Responder responder;

public SupportSystem() {
reader = new InputReader();
responder = new Responder();
}

9
Exemplo – Sistema de Apoio Técnico
public void start() {
▸ Classe SupportSystem – boolean finished = false;

método start printWelcome();

while(!finished) {
String input = reader.getInput();

if(input.startsWith("bye")) {
finished = true;
}
else {
String response = responder.generateResponse();
System.out.println(response);
}
}
printGoodbye();
}

10
Exemplo – Sistema de Apoio Técnico
▸ Classe SupportSystem – métodos printWelcome e printGoodbye

private void printWelcome() {


System.out.println("Welcome to the DodgySoft Technical Support System.");
System.out.println();
System.out.println("Please tell us about your problem.");
System.out.println("We will assist you with any problem you might
have.");
System.out.println("Please type 'bye' to exit our system.");
}

private void printGoodbye() {


System.out.println("Nice talking to you. Bye...");
}

11
Exemplo – Sistema de Apoio Técnico
▸ Sistema de Apoio Técnico:
▹ Tal como está não faz grande coisa
▹ A entrada de dados é sensível a espaços iniciais e finais e a caracteres
maiúsculos e minúsculos.
▹ A resposta é sempre igual
▹ Melhoramentos iniciais
▹ Ser mais flexível na entrada de dados
▹ Ter mais respostas. Podemos selecionar aleatoriamente uma delas.

12
Exemplo – Sistema de Apoio Técnico (2)
public void start() {
boolean finished = false;
▸ Classe SupportSystem printWelcome();

– método start (versão 2) while(!finished) {


String input = reader.getInput().trim().toLowerCase();
if(input.startsWith("bye")) {
finished = true;
}
Mais flexível na else {
leitura dos String response = responder.generateResponse();

dados. System.out.println(response);
}
}
printGoodbye();
}

13
Exemplo – Sistema de Apoio Técnico (2)
Para se escolher aleatoriamente
public class Responder { uma resposta

private Random randomGenerator;


private ArrayList<String> responses; ▸ Classe Responder
Para guardar uma lista de (versão 2)
public Responder() { respostas
randomGenerator = new Random();
responses = new ArrayList<String>();
fillResponses();
} Para criar a lista de
// Restante código respostas
}

14
Exemplo – Sistema de Apoio Técnico (2)
▸ Classe Responder (2) – método fillResponses

private void fillResponses() {

responses.add("That sounds odd. Could you describe that problem in more detail?");
responses.add("No other customer has ever complained about this before. \n" +
"What is your system configuration?");
responses.add("That's a known problem with Vista. Windows 7 is much better.");
responses.add("I need a bit more information on that.");
responses.add("Have you checked that you do not have a dll conflict?");
responses.add("That is explained in the manual. Have you read the manual?");
responses.add("Your description is a bit wishy-washy. Have you got an expert\n" +
"there with you who could describe this more precisely?");
responses.add("That's not a bug, it's a feature!");
responses.add("Could you elaborate on that?");

15
Exemplo – Sistema de Apoio Técnico (2)
public String generateResponse() {
▸ Classe Responder (2) –
int index = randomGenerator.nextInt(responses.size());
método
return responses.get(index);
generateResponse (2) }

16
Exemplo – Sistema de Apoio Técnico (2)
Welcome to the DodgySoft Technical Support System.

Please tell us about your problem. We will assist you


with any problem you might have. Please type 'bye'
▸ Utilização (2) to exit our system.
> my computer doesn't start
I need a bit more information on that.
> nothing shown
Your description is a bit wishy-washy. Have you got an
expert
there with you who could describe this more precisely?
> yes
Could you elaborate on that?
> nothing done
Could you elaborate on that?
> bye
Nice talking to you. Bye...

17
Exemplo – Sistema de Apoio Técnico (2)
▸ Sistema de Apoio Técnico (versão 2):
▹ Está melhor mas ainda tem alguns problemas:
▹ As respostas não dependem do texto que o utilizador introduziu.
▹ Melhoramentos finais
▹ Ter várias respostas associando cada uma delas a uma palavra que possa existir no
texto que o utilizador introduziu.
▹ Ter uma coleção com as palavras que o utilizador introduziu.
▹ Vamos usar uma nova classe de coleção – HashSet – para guardar as palavras do texto
que o utilizador inseriu.
▹ Vamos usar outra classe de coleção – HashMap – para guardar as ligações entre palavras
e respostas associadas.
18
Classe HashSet
▸ A classe de coleção HashSet:
▹ import java.util.Hashset
▹ A classe de coleção HashSet representa um conjunto (set)
▹ Nos conjuntos os elementos não se encontram ordenados.
▹ Neste caso deixamos de saber a posição dos elementos porque simplesmente não
se aplica (ao contrário das listas).
▹ Nos conjuntos não existem elementos repetidos.
▹ A classe HashSet é uma classe genérica
▹ Tal como na classe ArrayList, recebe o tipo dos elementos como parâmetro:
▹ Exemplo: HashSet<Person> , HashSet<Student>, etc.
19
Classe HashSet
▸ Métodos da classe HashSet:
▹ Muitos dos métodos da classe HashSet são semelhantes aos usados pela classe
ArrayList (e por outras classes de coleção):
▹ size – saber o número de elementos que existem,
▹ isEmpty – determinar se existem elementos,
▹ add – adicionar um elemento,
▹ Neste caso, se já existir, o elemento não é adicionado e retorna false
▹ remove – remover um elemento,
▹ Recebe como parâmetro o elemento a remover
▹ clear – remover todos os elementos,
▹ contains – verifica se um elemento existe na coleção.
20
Classe HashSet
▸ Métodos da classe HashSet:
▹ Possui alguns métodos que permitem fazer as tradicionais operações matemáticas
sobre conjuntos:
▹ contains – operação de pertença Î,
▹ addAll – operação de união È,
▹ retainAll – operação de interseção Ç,
▹ removeAll – operação de diferença –,
▹ containsAll – operação de contenção Ì.
Nota: os métodos acima que terminam em All recebem como parâmetro outra coleção.

21
Classe HashSet
System.out.println("*** HashSet professores");
HashSet<String> professors = new HashSet<>();
▸ Classe HashSet - Exemplo professors.add("Ana");
professors.add("Joao");
for(String s: professors) {
System.out.println(s);
}

HashSet<String> students = new HashSet<>();


students.add("Joao");
*** HashSet
students.add("Luis");
professores System.out.println("***** HashSet alunos");
Joao for(String s: students) {
Ana System.out.println(s);
***** HashSet alunos }
Joao
Luis

22
Classe HashSet
HashSet<String> persons = new HashSet<>(professors);
persons.addAll(students);
▸ Classe HashSet - Exemplo System.out.println("*******
HashSet pessoas = professores + alunos");
for (String s : persons) {
System.out.println(s);
}

******* HashSet pessoas = professores + alunos professors = new HashSet<>(persons);


Joao professors.removeAll(students);
Ana
System.out.println("*********
Luis
********* HashSet professores = pessoas - alunos HashSet professores = pessoas - alunos");
Ana for (String s : professors) {
System.out.println(s);
}

23
Classe HashSet
HashSet<String> c1 = new HashSet<>();
c1.add("A");c1.add("B"); // c1 = { A, B }
HashSet<String> c2 = new HashSet<>();
c2.add("A");c2.add("B");c2.add("C"); // c2 = { A, B, C }
System.out.println(c1.contains("A"));
System.out.println(c1.contains("C"));
▸ Classe HashSet – Exemplo 2 HashSet<String> union = new HashSet<>(c1);
union.addAll(c2);
System.out.println(union);
HashSet<String> intersection = new HashSet<>(c1);
intersection.retainAll(c2);
System.out.println(intersection);
true
HashSet<String> diference = new HashSet<>(c2);
false
[A, B, C] diference.removeAll(c1);
[A, B] System.out.println(diference);
[C] System.out.println(c2.containsAll(c1));
true

24
Exemplo – Sistema de Apoio Técnico (3)
▸ Classe InputReader –
public HashSet<String> getInput() { método getInput (3)
System.out.print("> "); // print prompt
String inputLine = reader.nextLine().trim().toLowerCase();

String[] wordArray = inputLine.split(" "); Divide o texto em palavras e


retorna-as como um array
(método da classe String)
HashSet<String> words = new HashSet<String>();
for(String word : wordArray) {
words.add(word);
}
return words;
Passa as palavras recebidas do array
}
para um Hashset e assim elimina as
repetições

25
Exemplo – Sistema de Apoio Técnico (3)
public void start() {
boolean finished = false;
▸ Classe SupportSystem – printWelcome();

método start (3) while(!finished) {


HashSet<String> input = reader.getInput();
if(input.contains("bye")) {
finished = true;
}
else {
String response = responder.generateResponse(input);
O conjunto de palavras que vêm no texto System.out.println(response);
escrito pelo utilizador é passado para o }
método generateResponse
}
printGoodbye();
}

26
Classe HashMap
▸ A classe de coleção HashMap:
▹ import java.util.HashMap
▹ A classe de coleção HashMap representa um mapeamento (map)
▹ Nos mapeamentos ou mapas são guardados pares de elementos.
▹ Um dos elementos do par é a chave o outro é o valor. Dizemos que a cada chave está associado um
valor.
▹ As chaves são únicas, não podendo haver repetições.
▹ Os valores podem ser repetidos desde que estejam associados a chaves diferentes.
▹ A classe HashMap é uma classe genérica
▹ No caso dos HashMap como temos pares de elementos devemos fornecer os tipos de cada um dos
elementos do par como parâmetros:
▹ Exemplo: HashMap<Integer,Person> , HashMap<String,String>, etc.

27
Classe HashMap
▸ A classe de coleção HashMap:
▹ A classe de coleção HashMap é utilizada para a associação entre
dois elementos, por exemplo:
▹ Num dicionário temos palavras (chaves) associadas a definições (valores).

▹ HashMap<String,String>
▹ Numa lista telefónica podemos ter números de telefone associados a pessoas.

▹ HashMap<Integer,Person>

28
Classe HashMap
▸ Métodos da classe HashMap:
▹ Métodos comuns a outras coleções:
▹ size – saber o número de elementos que existem,
▹ isEmpty – determinar se existem elementos,
▹ clear – remover todos os elementos.
▹ Métodos comuns:
▹ put – adicionar um par chave-valor,
▹ Recebe a chave e o valor como parâmetros. Se a chave já existir substitui o valor que estava guardado
pelo novo. Retorna o valor anterior ou null se a chave ainda não existia na coleção.
▹ get – vai buscar um valor associado a uma chave,
▹ Recebe como parâmetro a chave. Retorna o valor para essa chave ou null se a chave não existir.
29
Classe HashMap
▸ Métodos da classe HashMap:
▹ Métodos comuns (continuação):
▹ remove – remove da coleção um par chave-valor,
▹ Recebe como parâmetro a chave. Retorna o valor associado à chave ou null se a chave não existir.
▹ containsKey – verifica se já existe um elemento nas chaves,
▹ containsValue – verifica se já existe um elemento nos valores,
▹ keySet – retorna um conjunto com todas as chaves,
▹ values – retorna uma coleção com todos os valores,
▹ entrySet – retorna um conjunto de objetos Map.Entry<K,V>.
▹ Cada elemento do conjunto retornado tem a chave e o valor e é possível obter esses elementos usando,
respetivamente, os métodos getKey() e getValue()
30
Classe HashMap

▸ Classe HashMap – Exemplo HashMap<Integer, String> mapNames = new HashMap<>();


mapNames.put(13, "Maria");
mapNames.put(43, "Manuel");
mapNames.put(37, "Marco");
Pessoas: mapNames.put(23, "Maria"); //Valor repetido
37 - Marco mapNames.put(43, "Manuel Matos"); //Chave repetida
23 - Maria
43 - Manuel Matos
13 - Maria System.out.println("Pessoas:");
for (Integer i : mapNames.keySet()) {
System.out.println(i + " - " + mapNames.get(i));
}

Estamos a aceder aos


elementos através do
conjunto das chaves

31
Classe HashMap
HashMap<Integer, String> mapNames = new HashMap<>();
▸ Classe HashMap – Exemplo 2 mapNames.put(13, "Maria");
mapNames.put(43, "Manuel");
mapNames.put(37, "Marco");
mapNames.put(23, "Maria"); //Valor repetido
Pessoas: mapNames.put(43, "Manuel Matos"); //Chave repetida
Marco
Maria
Manuel Matos System.out.println("Pessoas:");
Maria
for (String name : mapNames.values()) {
System.out.println(name);
}
Estamos a aceder apenas
à coleção dos valores
(nomes)

32
Classe HashMap
▸ Classe HashMap – Exemplo 3
HashMap<Integer, String> mapNames = new HashMap<>();
mapNames.put(13, "Maria");
mapNames.put(43, "Manuel");
mapNames.put(37, "Marco");
Pessoas: mapNames.put(23, "Maria"); //Valor repetido
37 - Marco
23 - Maria mapNames.put(43, "Manuel Matos"); //Chave repetida
43 - Manuel Matos
13 - Maria System.out.println("Pessoas:");
for (Map.Entry pair : mapNames.entrySet()) {
System.out.println(pair.getKey() + " - "
+ pair.getValue());
Estamos a aceder ao
}
conjunto das entradas do
mapa

33
Exemplo – Sistema de Apoio Técnico (3)
public class Responder {
▸ Classe Responder (3) // Usado para associar palavras a respostas.
private HashMap<String, String> responseMap;
// Lista de respostas se não existirem palavras reconhecidas.
private ArrayList<String> defaultResponses;
private Random randomGenerator;

public Responder() {
responseMap = new HashMap<String, String>();
defaultResponses = new ArrayList<String>();
fillResponseMap();
fillDefaultResponses();
randomGenerator = new Random();
}
// restantes métodos

34
Exemplo – Sistema de Apoio Técnico (3)
▸ Classe Responder (3) – métodos fillDefaultResponses e pickDefaultResponse

private void fillDefaultResponses() {


defaultResponses.add("That sounds odd. Could you describe that problem in more" +
"detail?");
defaultResponses.add("No other customer has ever complained about this before. \n" +
"What is your system configuration?");
defaultResponses.add("That sounds interesting. Tell me more...");
defaultResponses.add("I need a bit more information on that.");
defaultResponses.add("Have you checked that you do not have a dll conflict?");
defaultResponses.add("That is explained in the manual. Have you read the manual?");
defaultResponses.add("Your description is a bit wishy-washy. Have you got an expert\n“
+ "there with you who could describe this more precisely?");
defaultResponses.add("That's not a bug, it's a feature!");
defaultResponses.add("Could you elaborate on that?");
}
private String pickDefaultResponse() {
int index = randomGenerator.nextInt(defaultResponses.size());
return defaultResponses.get(index);
}

35
Exemplo – Sistema de Apoio Técnico (3)
▸ Classe Responder (3) – método fillResponsesMap
private void fillResponseMap() {
responseMap.put("crash",
"Well, it never crashes on our system. It must have something\n" +
"to do with your system. Tell me more about your configuration.");
responseMap.put("crashes",
"Well, it never crashes on our system. It must have something\n" +
"to do with your system. Tell me more about your configuration.");
responseMap.put("slow",
"I think this has to do with your hardware. Upgrading your processor\n“
+ "should solve all performance problems. Have you got a problem with\n“
+ "our software?");
responseMap.put("windows",
"This is a known bug to do with the Windows operating system. Please\n" +
"report it to Microsoft. There is nothing we can do about this.");
responseMap.put("bug",
"Well, you know, all software has some bugs. But our software engineers\n“
+ "are working very hard to fix them. Can you describe the problem a bit\n" +
"further?");

// outras associações omitidas

36
Exemplo – Sistema de Apoio Técnico (3)
public String generateResponse(HashSet<String> words) {
▸ Classe Responder (3) – método for (String word : words) {
generateResponse String response = responseMap.get(word);

if(response != null) {
return response;

return pickDefaultResponse();
}

37
Exemplo – Sistema de Apoio Técnico (3)
Welcome to the DodgySoft Technical Support System.

▸ Utilização (3) Please tell us about your problem.


We will assist you with any problem you might have.
Please type 'bye' to exit our system.
> i have a hardware problem
No other customer has ever complained about this before.
What is your system configuration?
> a windows computer
This is a known bug to do with the Windows operating system.
Please
report it to Microsoft. There is nothing we can do about
this.
> bye
Nice talking to you. Bye...

38
Exemplo – Coleção de Cromos
▸ Requisitos da aplicação:
▹ Fazer a gestão de uma coleção de cromos.
▹ Cada cromo é caracterizado pelo seu número e pelo seu estado (bom,
razoável, mau).
▹ Na gestão dos cromos deverá ser definido o nome da coleção e o
número de cromos da coleção completa. Os melhores cromos devem
ser guardados numa coleção sem repetições para irem para a
caderneta. Os repetidos serão guardados separadamente.
▹ Na gestão precisamos saber quantos cromos temos, quantos faltam,
adicionar cromos, lista de repetidos, lista dos que temos (números).

39
Exemplo – Coleção de Cromos
public enum CardState {
▸ Classe TradingCard }
GOOD, REASONABLE, POOR

public class TradingCard {

private int number;


private CardState state;

public TradingCard(int number, CardState state) {


this.number = number;
this.state = state;
}

// restante código omitido


}

40
Exemplo – Coleção de Cromos
public class TradingCard {
// restante código omitido
▸ Classe TradingCard public CardState getState() {
return state;
}

public void setState(CardState state) {


this.state = state;
}

public int getNumber() {


return number;
}

public String toString() {


return "#" + number + " (" + state + ")";
}
}

41
Exemplo – Coleção de Cromos
public class TradingCardCollection { Lista para os repetidos

private String title;


private int largestNumber;
private ArrayList<TradingCard> repeated;
private HashSet<TradingCard> cards; Conjunto para os cromos
da caderneta
public TradingCardCollection(String title, int largestNumber) {

this.title = title;
this.largestNumber = largestNumber;
repeated = new ArrayList<>();
cards = new HashSet<>();
▸ Classe
} TradingCardCollection
// restante código omitido
}

42
Exemplo – Coleção de Cromos
▸ Classe private boolean validateTradingCard(TradingCard tradingCard) {
return tradingCard != null &&
TradingCardCollection – tradingCard.getNumber() > 0 &&
tradingCard.getNumber() <= largestNumber ;
validateTradingCard e }

addTradingCard
public void addTradingCard(TradingCard tradingCard) {
if (validateTradingCard(tradingCard)) {
if (!cards.add(tradingCard)) {
Se já existir na repeated.add(tradingCard);
coleção }
}
adicionamos aos }
repetidos

43
Exemplo – Coleção de Cromos
▸ Classe
public String toString() {

TradingCardCollection – String result = "Coleção " + title;


result += "\nCromos:\n";
for (TradingCard tradingCard : cards) {
toString result += tradingCard + " ";
}
result += "\nRepetidos:\n";
for (TradingCard tradingCard : repeated) {
result += tradingCard + " ";
}
return result;
}

44
Exemplo – Coleção de Cromos
public class Program {

▸ Teste simples da aplicação public static void main() {


TradingCard card1 = new TradingCard(12, CardState.GOOD);
TradingCard card2 = new TradingCard(12, CardState.GOOD);
TradingCard card3 = new TradingCard(5, CardState.POOR);
TradingCard card4 = new TradingCard(6, CardState.GOOD);

TradingCardCollection cards = new TradingCardCollection("Teste", 20);


cards.addTradingCard(card1);
cards.addTradingCard(card2);
cards.addTradingCard(card3);
cards.addTradingCard(card4);

System.out.println(cards);
}
}

45
Exemplo – Coleção de Cromos

46
Exemplo – Coleção de Cromos
▸ Teste simples da aplicação

TradingCard card1 = new TradingCard(12, CardState.GOOD);


TradingCard card2 = new TradingCard(12, CardState.GOOD);
TradingCard card3 = new TradingCard(5, CardState.POOR);
TradingCard card4 = new TradingCard(6, CardState.GOOD); O #12 não
deveria estar
TradingCardCollection cards = new TradingCardCollection("Teste", 20); repetido???
cards.addTradingCard(card1);
cards.addTradingCard(card2);
cards.addTradingCard(card3);
cards.addTradingCard(card4);

System.out.println(cards);

Coleção Teste
Cromos:
#6 (GOOD) #12 (GOOD) #5 (POOR) #12 (GOOD)
Repetidos:

47
Classe HashSet
▸ A classe HashSet usa um método chamado equals para comparar dois elementos.
▹ O método equals, tal como o método toString existe em todos os objetos e é
usado para comparar dois objetos. Por omissão, compara as referências dos
objetos.
▹ A assinatura deste método é:
public boolean equals(Object obj)
▹ O objeto recebido (tipo Object) pode ser de qualquer classe mas espera-se que
seja da classe do objeto a comparar porque senão não será igual (segundo if
do slide seguinte).
48
Classe HashSet

▸ Modo de criação do método


equals (para qualquer classe) public boolean equals(Object obj) {
if (obj == null) {
▹ Modelo: }
return false;

if (getClass() != obj.getClass()) {
return false;
}
final ClasseDoObjeto other = (ClasseDoObjeto) obj;
O getClass à
semelhança do return //teste da igualdade entre os dois objetos (this e other;
toString, equals e }
hashCode é outro dos
métodos que todas as
classes têm por omissão

49
Classe Cromo

▸ O método equals que queremos public boolean equals(Object obj) {


if (obj == null) {
que seja utilizado na situação atual, }
return false;

if (getClass() != obj.getClass()) {
deverá ser definido na classe }
return false;

final TradingCard other = (TradingCard) obj;


TradingCard e será (apenas
return this.number == other.number;
}
compara o números dos cromos):

50
Classe HashSet
▸ A classe HashSet usa o método equals para comparar dois elementos mas não é ainda
suficiente para que a comparação é seja feita eficientemente para todos os elementos da
coleção. Para que isso aconteça é necessário fornecer mais um método: o método hashCode
▹ O método hashCode, tal como os métodos toString e equals existe também em todos os
objetos e tem uma implementação por omissão que será necessária alterar na maioria
dos casos.
▹ A assinatura deste método é:
public int hashCode()
▹ O valor inteiro retornado deverá ser diferente sempre que os objetos a comparar
forem diferentes.
51
Classe HashSet – Implementação
através de hash table
▸ Um HashSet é implementado através de uma hash table. Uma hash table pode ser vista como um array onde os
elementos são colocados na posição que se obtém pela chamada ao método hashCode (implementação hipotética
do método que adiciona elementos ao conjunto):
public boolean add (Object object) {
int position = object.hashCode();
if (values[position] == null) { //ainda não foi colocado
values[position] = object;
return true;
}
return false;
}
▸ Com esta abordagem a determinação da existência de um elemento no conjunto é muito rápida: basta executar o
método hashCode, no elemento, e verificar se a posição respetiva está ocupada, sem ser necessário percorrer todo
o conjunto, comparando elemento a elemento.
52
Método hashCode
▸ O método hashCode não garante que é devolvido um valor diferente para cada objeto. Tal seria impossível, pois podem
existir mais objetos que os valores disponíveis na gama dos inteiros e o algoritmo a implementar seria tão complexo que
não seria eficiente.

▸ Assume-se que método hashCode devolve um valor que seja aleatoriamente distribuído, por forma a reduzir a hipótese
de repetição (chamada "colisão"). Havendo a certeza que elas vão acabar por existir.

▸ Assim um HashSet é implementado através de uma hash table onde, em cada posição não será colocado o elemento
mas é colocada uma sequência de elementos (designada por bucket - balde) que contêm o mesmo hashCode. Desta
forma a existência de dois objetos com o mesmo hashCode não implica que um vá substituir o outro: ficam ambos
colocados na mesma sequência.

53
Uso do método hashCode
▸ Ao se introduzir um elemento num HashSet o sistema começa por determinar a posição do elemento na hash table
através da chamada ao método hashCode, percorrendo em seguida a sequência de elementos comparando-os
através do método equals.

▸ Por esta razão os métodos equals e hashCode estão intimamente relacionados, não podendo fazer a redefinição
de um sem redefinir o outro (se apenas redefinirmos o equals o hashCode continua a indicar posições diferentes
na hash table para os objetos, permitindo repetições).

▸ Devendo na sua redefinição envolver, em ambos os métodos, os mesmos atributos da classe.

54
Definição do método hashCode
▸ Na definição do método hashCode do Java, dada em
http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html é dito que:

▹ Durante a uma execução de uma aplicação Java, a chamada ao método hashCode, para o mesmo objeto, deve devolver
valores iguais. Tal não é garantido para execuções distintas (é preciso ter especial cuidado se forem armazenados hashCode
em bases de dados e posteriormente obtidos ou se tivermos aplicações distribuídas por diferentes máquinas);

▹ Se dois objetos são equals então devem ter o mesmo hashCode;

▹ Não é obrigatório que dois objetos que não sejam equals tenham hashCode diferentes. Mas uma boa implementação produz
valores tendencialmente distintos para aumentar a performance nas hash table.

55
Exemplo – Coleção de Cromos
public int hashCode() {
return this.number;
▸ Classe TradingCard – }
public boolean equals(Object obj) {
equals e hashCode if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TradingCard other = (TradingCard) obj;
return this.number == other.number;
}

56
Exemplo – Coleção de Cromos
▸ Teste simples da aplicação TradingCard card1 = new TradingCard(12, CardState.GOOD);
TradingCard card2 = new TradingCard(12, CardState.GOOD);
TradingCard card3 = new TradingCard(5, CardState.POOR);
TradingCard card4 = new TradingCard(6, CardState.GOOD);
TradingCard card5 = new TradingCard(12, CardState.REASONABLE);

TradingCardCollection cards = new TradingCardCollection("Teste", 20);


Coleção Teste cards.addTradingCard(card1);
Cromos: cards.addTradingCard(card2);
#5 (POOR) #6 (GOOD) #12 (GOOD) cards.addTradingCard(card3);
Repetidos: cards.addTradingCard(card4);
cards.addTradingCard(card5);
#12 (GOOD) #12 (REASONABLE)
System.out.println(cards);

57
Exemplo – Coleção de Cromos

▸ Classe TradingCardCollection – public boolean isFull() {

return cards.size() == largestNumber;


isFull, isEmpty e totalCards }

public boolean isEmpty() {

return cards.isEmpty();

public int totalCards() {

return cards.size() + repeated.size();

58
Exemplo – Coleção de Cromos
public void addTradingCards(TradingCard[] cards) {
Classe TradingCardCollection – for (TradingCard card : cards) {
addTradingCard(card);
addTradingCards e missingCards }
}
public HashSet<Integer> missingCards() {
HashSet<Integer> missing = new HashSet<>();
for (int i = 1; i <= largestNumber; i++) {
missing.add(i);
}
for (TradingCard card : cards) {
missing.remove(new Integer(card.getNumber()));
}
return missing;
}

59
Exemplo – Coleção de Cromos

▸ Classe TradingCardCollection –
public ArrayList<TradingCard> repetitionsOfCard(int number) {
ArrayList<TradingCard> repetitions = new ArrayList<>();
repetitionsOfCard for (TradingCard card : repeated) {
if (card.getNumber() == number) {
repetitions.add(card);
}
}
return repetitions;
}

60
Exemplo – Coleção de Cromos
public void optimize() {
HashSet<TradingCard> notGood = new HashSet<>(); ▸ Classe TradingCardCollection –
for (TradingCard card : cards) {
if (card.getState() != CardState.GOOD) { optimize
notGood.add(card);
}
}
for (TradingCard card : notGood) {
for(int i=0; i<repeated.size(); i++){
TradingCard iCard = repeated.get(i);
if (iCard.equals(card) && iCard.isInBetterStateThan(card)) {
cards.remove(card);
cards.add(iCard);
repeated.set(i, card);
break;
}
}
} Novo método:
} isInBetterStateThan

61
Exemplo – Coleção de Cromos
▸ Classe TradingCard –
isInBetterStateThan public boolean isInBetterStateThan(TradingCard card) {

return (this.state == CardState.GOOD &&


card.getState() != CardState.GOOD) ||

(this.state == CardState.REASONABLE &&


card.getState() == CardState.POOR);
}

62
Exemplo – Coleção de Cromos
TradingCard[] cards = new TradingCard[10];
cards[0] = new TradingCard(1, CardState.GOOD);
cards[1] = new TradingCard(2, CardState.GOOD);
cards[2] = new TradingCard(2, CardState.REASONABLE);

▸ Teste simples da aplicação


cards[3] = new TradingCard(3, CardState.POOR);
cards[4] = new TradingCard(4, CardState.GOOD);
cards[5] = new TradingCard(7, CardState.GOOD);
cards[6] = new TradingCard(8, CardState.REASONABLE);
cards[7] = new TradingCard(8, CardState.GOOD);
cards[8] = new TradingCard(8, CardState.REASONABLE);
cards[9] = new TradingCard(8, CardState.POOR);

TradingCardCollection cars = new TradingCardCollection("Carros 2015", 10);

cars.addTradingCards(cards);
cars.addTradingCard(new TradingCard(3, CardState.REASONABLE));

System.out.println(cars);

System.out.println("Faltam: ");
for(int number : cars.missingCards())
System.out.print(number + ", ");
System.out.println();

63
Exemplo – Coleção de Cromos
cars.optimize();

▸ Teste simples da aplicação System.out.println(cars);

Coleção Carros 2015


Cromos:
#1 (GOOD) #2 (GOOD) #3 (POOR) #4 (GOOD) #7 (GOOD) #8 (REASONABLE)
Repetidos:
#2 (REASONABLE) #8 (GOOD) #8 (REASONABLE) #8 (POOR) #3 (REASONABLE)
Faltam:
5, 6, 9, 10,
Coleção Carros 2015
Cromos:
#1 (GOOD) #2 (GOOD) #3 (REASONABLE) #4 (GOOD) #7 (GOOD) #8 (GOOD)
Repetidos:
#2 (REASONABLE) #8 (REASONABLE) #8 (POOR) #3 (POOR) #8 (REASONABLE)

64
Exemplo – Coleção de Cromos (2)
Lista para os repetidos
public class TradingCardCollection {

private String title;


private int largestNumber;
Mapa para os cromos da
private ArrayList<TradingCard> repeated;
caderneta
private HashMap<Integer, TradingCard> cards;

public TradingCardCollection(String title, int largestNumber) {


this.title = title;
this.largestNumber = largestNumber;
repeated = new ArrayList<>(); ▸ Classe
cards = new HashMap<>();
} TradingCardCollection
// restante código omitido
com HashMap
}

65
Exemplo – Coleção de Cromos
private boolean validateTradingCard(TradingCard tradingCard) {
return tradingCard != null &&
▸ Classe TradingCardCollection com tradingCard.getNumber() > 0 &&

HashMap – }
tradingCard.getNumber() <= largestNumber ;

validateTradingCard e
public void addTradingCard(TradingCard tradingCard) {
addTradingCard if (validateTradingCard(tradingCard)) {
if (!cards.containsKey(tradingCard.getNumber())) {
cards.put(tradingCard.getNumber(), tradingCard);
} else {
repeated.add(tradingCard);
}
Se já existir na
}
coleção }
adicionamos aos
repetidos

66
Exemplo – Coleção de Cromos (2)
▸ Classe TradingCardCollection public HashSet<Integer> missingCards() {
HashSet<Integer> missing = new HashSet<>();
com HashMap – missingCards
for (int i = 1; i <= largestNumber; i++) {

missing.add(i);

missing.removeAll(cards.keySet());

return missing;

67
Bibliografia

▸ Objects First with Java (6th Edition), David Barnes & Michael

Kölling, Pearson Education Limited, 2016

▹ Capítulo 6 (6.1 a 6.9)

68
Programação Orientada por Objetos

Exceções

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Aplicação Agenda de Endereços


▸ Gestão de Erros

▸ Mecanismo de Exceções
▸ Definição de Exceções

2
Agenda de
Endereços
▸ Exceções
Exemplo – Address Book

▸ Criar uma aplicação para guardar contactos.


▹ Cada contacto regista a informação do nome, telefone e endereço.
▹ Deve ser possível efetuar as operações habituais de criação, listagem,
alteração e remoção de contactos (operações CRUD).
▹ Deve existir uma forma de procurar contactos pelo nome ou telefone.
▹ Criar uma interface de consola para a aplicação.

4
Exemplo – AddressBook
▸ Diagrama de classes da aplicação Address Book:

5
Exemplo – AddressBook
▸ Classes principais da aplicação AddressBook:
▹ ContactDetails – Informação do contacto.
▹ AddressBook – Lista de contactos.

▸ Classes da interface
▹ CommandWords – Define os comandos que podem ser dados
na consola.
▹ Parser – Lê a informação da consola e interpreta-a
retornando-a como um objeto Command.
▹ AddressBookTextInterface – A aplicação em ambiente
de consola.
▹ AddressBookTextDemo – Corre uma demonstração com
alguns contactos já definidos.

6
Exemplo – AddressBook
▸ Classe ContactDetails
public class ContactDetails implements Comparable<ContactDetails> {
private String name;
private String phone;
private String address;
public ContactDetails(String name, String phone, String address) {
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = ""; }
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
}
// Continua…
7
Exemplo – AddressBook
▸ Classe ContactDetails – métodos seletores e toString

public String getName() {


return name;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
public String toString() {
return name + "\n" + phone + "\n" + address;
}

8
Exemplo – AddressBook
▸ Classe ContactDetails – métodos equals e hashCode
public boolean equals(Object other) {
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}

public int hashCode() {


int code = 17; code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
9
Exemplo – AddressBook
▸ Classe ContactDetails – método compareTo Implementação da interface
Comparable<ContactDetails>

public int compareTo(ContactDetails otherDetails) {


int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
Vai permitir a ordenação por:
return comparison; 1-nome, a seguir
} 2-telefone e a seguir
3-endereço
return address.compareTo(otherDetails.getAddress());
}

10
Exemplo – AddressBook
▸ Classe AddressBook e método addDetails
public class AddressBook { Armazena os contactos
numa coleção TreeMap
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;

public AddressBook() { Usa como chaves


book = new TreeMap<String, ContactDetails>(); simultaneamente o nome e
numberOfEntries = 0; o telefone para facilitar a
} procura

public void addDetails(ContactDetails details){


book.put(details.getName(), details);
book.put(details.getPhone(), details); Porquê?
numberOfEntries++;
}
// Continua… Como tem duas entradas por
contacto usa separadamente um
contador de contactos
11
Exemplo – AddressBook
▸ Classe AddressBook – métodos getDetails, KeyInUse e changeDetails

public ContactDetails getDetails(String key) {


return book.get(key);
}

public boolean keyInUse(String key) {


return book.containsKey(key);
}

public void changeDetails(String oldKey, ContactDetails details){


removeDetails(oldKey);
addDetails(details);
}

12
Exemplo – AddressBook
▸ Classe AddressBook – métodos getNumberOfEntries e removeDetails

public int getNumberOfEntries() {


return numberOfEntries;
}

public void removeDetails(String key) {


ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
} Como funciona?

13
Exemplo – AddressBook
▸ Classe AddressBook – método listDetails

public String listDetails() {


StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<ContactDetails>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details);
allEntries.append('\n’);
allEntries.append('\n’);
}
return allEntries.toString(); Como existem duas chaves é
necessário eliminar as
} duplicações de contactos!

14
Exemplo – AddressBook
▸ Classe AddressBook – método search
public ContactDetails[] search(String keyPrefix) {
List<ContactDetails> matches = new LinkedList<ContactDetails>();
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) { Chaves maiores ou
matches.add(book.get(key)); iguais ao prefixo
}
else {
endOfSearch = true;
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results; Devolve um array com
os contactos
}
15
Exemplo – Addressbook
▸ Análise da aplicação AddressBook
▹ A aplicação está funcional mas estamos a assumir que tudo corre bem ou não devemos assumir isso?
▹ Neste caso um objeto AddressBook é um objeto servidor típico.
▹ Não inicia ações, toda a sua atividade é em resposta a pedidos de clientes.
▹ Se pensarmos que o objeto servidor terá clientes que poderão cometer erros inadvertidamente ou mesmo intencionalmente, a
implementação nestes casos terá que ser diferente.
▹ A maior vulnerabilidade num objeto servidor está nos argumentos dos métodos
▹ Os argumentos dos construtores que inicializam o estado do objeto
▹ Os argumentos dos métodos que contribuem para o comportamento do objeto
▹ Devemos colocar as seguintes questões para lidar com as vulnerabilidades:
▹ Que verificações devem ser feitas nos métodos do servidor?
▹ Como reportar os erros aos clientes?
▹ Como devem os clientes antecipar problemas e falhas nos pedidos ao servidor?
▹ Como é que os clientes devem lidar com as falhas nos pedidos ao servidor?
16
Gestão de Erros

▸ Exceções

17
Exemplo – Address Book
▸ Criar uma aplicação para guardar contactos.
▹ Cada contacto regista a informação do nome, telefone e
endereço.
▹ Deve ser possível efetuar as operações habituais de criação,
listagem, alteração e remoção de contactos (operações CRUD).
▹ Deve existir uma forma de procurar contactos pelo nome ou
telefone.
▹ Criar uma interface de consola para a aplicação.

18
Exemplo – Addressbook
▸ Análise da aplicação AddressBook
▹ A aplicação está funcional mas estamos a assumir que tudo corre bem ou não devemos
assumir isso?
▹ A vulnerabilidade nos argumentos dos métodos
▹ Os argumentos dos construtores que inicializam o estado do objeto
▹ Os argumentos dos métodos que contribuem para o comportamento do objeto
▹ Devemos colocar as seguintes questões para lidar com as vulnerabilidades:
▹ Que verificações devem ser feitas nos métodos do servidor?
▹ Como reportar os erros aos clientes?
▹ Como devem os clientes antecipar problemas e falhas nos pedidos ao servidor?
▹ Como é que os clientes devem lidar com as falhas nos pedidos ao servidor?
19
Exemplo – Addressbook
▸ Análise da aplicação AddressBook – método removeDetails
▹ Exemplo de uma vulnerabilidade:
1. Exemplo - chamado com null:
addressBook.removeDetails(null)
public void removeDetails(String key) {
ContactDetails details = book.get(key); 2. book.get(key) retorna null:
details fica com o valor null
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--; } 3. O programa termina com uma exceção na chamada a details.getName():
java.lang.NullPointerException
at java.util.TreeMap.getEntry(TreeMap.java:347)
at java.util.TreeMap.get(TreeMap.java:278)
1. Se a chave não existir book.get(key) retorna null at AddressBook.removeDetails(AddressBook.java:121)

▹ Não é aqui que está o problema


2. A seguir details.getName() origina um erro durante a execução
▹ Neste caso details tem o valor null, a chamada a métodos a partir deste objeto leva a que o programa termine com uma mensagem de exceção
▹ De quem é a culpa? Do servidor que não verificou o argumento passado ou do cliente que passou um valor errado?

20
Exemplo – Addressbook
▸ Análise da aplicação AddressBook
▹ No exemplo:
▹ Criamos um objeto AddressBook.
▹ Removemos um contacto ( removeDetails )
▹ A aplicação reporta um erro na execução.
▹ De quem é a culpa deste erro?
▹ É preferível anteciparmos esta situação do que passar por este problema.
▹ A maior vulnerabilidade num objeto servidor está nos argumentos dos métodos
▹ Os argumentos dos construtores que inicializam o estado do objeto
▹ Os argumentos dos métodos que contribuem para o comportamento do objeto
▹ A verificação dos argumentos é o que se chama uma medida defensiva
21
Exemplo – Addressbook
▸ Análise da aplicação AddressBook – método removeDetails
▹ Uma solução simples neste caso é fazer a verificação e não agir se a chave a procurar não existir.

public void removeDetails(String key) {


if( keyInUse(key) ) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
}

22
Exemplo – Addressbook
▸ Análise da aplicação AddressBook – outros métodos
▹ Problemas idênticos existem noutros métodos:
▹ void addDetails(ContactDetails details)
▹ Não verifica se o parâmetro details vem com null
▹ void changeDetails(String oldKey, ContactDetails details)
▹ Oldkey devia existir e details não deveria ter null
▹ ContactDetails[] search(String keyPrefix)
▹ keyPrefix não deve vir a null
▸ Mesmo que se protejam os métodos com verificações o problema não fica totalmente resolvido. É
conveniente em casos destes avisar a aplicação cliente ou mesmo o utilizador. Qual a melhor
maneira de o fazer?
▹ A resposta é: depende! Não existe uma solução única. 23
Reporte de erros
▸ Solução 1: Notificar o utilizador
▹ Através duma mensagem de erro escrita no ecrã ou numa janela de alerta.
▸ Problemas da solução:
▹ Estamos a assumir que existe um utilizador humano com acesso à mensagem.
▹ Nem sempre é verdade. A aplicação do utilizador pode estar a correr num computador
diferente daquele que tem os dados. Pode não existir acesso a um dispositivo de visualização.
▹ Mesmo que o utilizador tenha acesso à informação do erro será que ele pode de alguma forma
corrigi-lo?
▹ A maior parte das vezes o utilizador não tem meios para corrigir o erro.
▹O que pode fazer um utilizador se estiver num terminal de multibanco e receber uma mensagem
NullPointerException ? J

24
Reporte de erros
▸ Solução 2: Notificar a aplicação cliente através de um valor de retorno do método
que está a ser chamado.
▹ No objeto servidor:

public boolean removeDetails(String key) {


if (keyInUse(key)) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
return true; Removido com sucesso
}
else {
return false;
}
} Falhou a remoção
25
Reporte de erros
▸ Solução 2: Notificar a aplicação cliente através de um valor de retorno do método que está a ser
chamado.
▹ No método cliente:
Removido com sucesso
if(contacts.removeDetails ( "…")) { Continuar normalmente
// Contacto removido com sucesso.
// Continuar normalmente. …
}
else { Falhou a remoção
// Falhou a remoção. Tentar recuperar do problema
// Tentar uma ação para recuperar do
problema se for possível
}

26
Reporte de erros
▸ Solução 2: Notificar a aplicação cliente através de um valor de retorno do
método que está a ser chamado.
▸ Problemas da solução:
▹ Por vezes o valor de retorno não permite a utilização dum valor específico para o erro.
▹ Exemplo: um método double getBalance(int accountNumber) que retorna o saldo duma
conta bancária. Como definir um valor de retorno a reportar que o número da conta está errado?
▹ Mesmo que exista o retorno com a informação do erro como se garante que a aplicação cliente vai
verificar e utilizar esse valor de retorno?
▹ A aplicação pode decidir ignorar o valor de retorno do método:

contacts.removeDetails ("…")); // Sem teste ao retorno


// Contacto removido com ou sem sucesso.
// Continuar normalmente.
27
mecanismo de
Exceções
▸ Exceções

28
Exemplo – Addressbook
▸ Análise da aplicação AddressBook
▹ A aplicação está funcional mas estamos a assumir que tudo corre bem ou não devemos assumir isso?
▹ A vulnerabilidade nos argumentos dos métodos
▹ Os argumentos dos construtores que inicializam o estado do objeto
▹ Os argumentos dos métodos que contribuem para o comportamento do objeto
▹ Devemos colocar as seguintes questões para lidar com as vulnerabilidades:
▹ Que verificações devem ser feitas nos métodos do servidor?
▹ Como reportar os erros aos clientes?
▹ Como devem os clientes antecipar problemas e falhas nos pedidos ao servidor?
▹ Como é que os clientes devem lidar com as falhas nos pedidos ao servidor?

29
Reporte de erros
▸ Solução 1: Notificar o utilizador
▹ Através duma mensagem de erro escrita no ecrã ou numa janela de alerta.
▸ Problemas da solução:
▹ Estamos a assumir que existe um utilizador humano com acesso à mensagem.
▹ Mesmo que o utilizador tenha acesso à informação do erro será que ele pode de alguma forma corrigi-lo?
▸ Solução 2: Notificar a aplicação cliente através de um valor de retorno do método que está a ser chamado.
▸ Problemas da solução:
▹ Por vezes o valor de retorno não permite a utilização dum valor específico para o erro.
▹ Mesmo que exista o retorno com a informação do erro como se garante que a aplicação cliente vai verificar e
utilizar esse valor de retorno?

30
Reporte de erros – Exceções
▸ Solução 3: Notificar a aplicação cliente através da utilização do mecanismo de Exceções.
▹ Em caso de erro é lançada uma exceção a informar do erro e a aplicação cliente é obrigada a tratar esse
erro se não quiser evitar que a aplicação termine abruptamente reportando a informação da exceção.
▸ Uma Exceção é um sinal gerado pela máquina virtual de Java em tempo de execução, que é enviado ao
programa indicando a ocorrência de um erro recuperável.
▸ Funcionamento do mecanismo de exceções:
▹ Quando existe um erro é criado um objeto com a informação do problema.
▹ A aplicação é interrompida e é passado à aplicação o objeto criado com a informação do problema. Diz-
se que foi lançada uma Exceção.
▹ O controlo do programa passa depois para um bloco de tratamento do erro caso este tenha sido criado,
caso contrário a aplicação é interrompida e é reportada a exceção ocorrida.
▹ Depois do erro tratado a execução da aplicação continua normalmente.
31
Reporte de erros
▸ Exemplo do lançamento duma exceção:

public ContactDetails getDetails(String key) {


if (key == null) {
throw new IllegalArgumentException("null key in getDetails");
}
return book.get(key);
Lançamento de uma
} exceção

▹ Neste caso a exceção é lançada pelo método servidor


▹ Também é possível ser a máquina virtual do Java a lançar internamente a exceção
▹ Foi o caso no exemplo anterior com a exceção NullPointerAssigment que foi lançada pela JVM
32
Lançamento de uma exceção
Palavra reservada Mensagem de erro
throw null key in getDetails

throw new IllegalArgumentException("null key in getDetails");

Nome da classe da exceção


IllegalArgumentException
▹ Lançamento de uma exceção
▹ É criado o objeto com a exceção:
▹ new ExceptionType("...")
▹ O objeto da exceção é “lançado” (thrown):
▹ throw ...
▹ Na documentação em Javadoc:
▹ @throws ExceptionType explicação do motivo …

33
Classe das exceções
▸ As exceções lançadas são representadas por classes que formam uma hierarquia com
base na classe Exception:
▹ Para cada tipo de exceção existe uma classe própria
▹ Todas as classes de exceção têm por convenção o sufixo Exception

34
Consequências das exceções
▸ Quando uma exceção é lançada o método onde está termina imediatamente.
▹ Não existe valor de retorno
▹ O controlo da aplicação não volta ao ponto onde o método foi chamado
▹ Neste caso a aplicação não pode continuar ignorando o que aconteceu
▹ Mas a aplicação pode capturar a exceção

Chamada ao método getDetails


AddressBook book = new AddressBook(); passando null

ContactDetails contact = book.getDetails(null);


System.out.println("Details: " + contact);
O programa não continua na
instrução seguinte
public ContactDetails getDetails(String key) {
if (key == null) {
throw new IllegalArgumentException("null key in getDetails");
}
return book.get(key); Lançamento da exceção:
} O método termina imediatamente
sem valor de retorno 35
Exemplo – AddressBook
▸ Através do lançamento de exceções podemos proteger os nossos métodos de uma utilização errada

public void changeDetails(String oldKey, ContactDetails details){


if(details == null) {
throw new IllegalArgumentException("Null details passed to changeDetails.");
}
if(oldKey == null){
throw new IllegalArgumentException("Null key passed to changeDetails.");
}
if(keyInUse(oldKey)){
removeDetails(oldKey);
addDetails(details);
}
}

▸ Nestes casos estamos a utilizar as exceções que existem no Java.


▹ É o caso da exceção IllegalArgumentException
▸ Se a exceção que lançamos não for capturada o programa será terminado.
36
Exemplo – AddressBook
▸ Através do lançamento de exceções podemos igualmente impedir que os objetos sejam criados
public ContactDetails(String name, String phone, String address) {
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
Lançamento da
address = "";
exceção:
} O objeto não é criado
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
if(this.name.length() == 0 && this.phone.length() == 0) {
throw new IllegalStateException("Either the name or phone must not be blank.");
}
}
Exceção do tipo IllegalStateException:
Exceção do Java não verificada
37
Tratamento de exceções
▸ Se quisermos impedir que o programa termine temos de capturar a exceção que ocorreu. Neste caso estamos a tratar a
exceção.
▸ O tratamento de exceções é feito usando um bloco try-catch
▹ O código que é verificado quanto à ocorrência de exceções está dentro dum bloco try
▹ O código que é executado quando a exceção ocorre está dentro de um bloco catch
Palavra Bloco de instruções
reservada try com verificação
try {
// Instruções com verificação de ocorrência de exceção
}
Palavra reservada Objeto “e” com a
catch exceção ocorrida
Bloco de tratamento
da exceção
catch(Exception e) {
// Código do tratamento da exceção
}
38
Tratamento de exceções
▸ Exemplo do tratamento duma exceção:
1. Exceção lançada daqui
AddressBook book = new AddressBook();
try {
ContactDetails contact = book.getDetails(null);
System.out.println("Details: " + contact);
} 2. Execução transferida para aqui
catch(IllegalArgumentException e) {
System.out.println("A chave deu problemas: " + e.getMessage());
//...
}

e – vem com o objeto de exceção criado em getDetails

O método getMessage() retorna a mensagem


de erro que foi criada com a exceção

39
Tratamento de exceções
▸ Podem existir vários blocos catch de tratamento de exceções
▹ Cada bloco processa o tratamento de um tipo de exceção
▹ Apenas um dos blocos é executado quando ocorre uma exceção dentro do bloco try
▹ O primeiro que for do tipo da exceção que foi lançada será o que é executado
▹ Como existe uma hierarquia de exceções, as exceções mais genéricas devem ser as últimas a ser capturadas
try {
...
ref.process();
...
}
catch(EOFException e) {
// código de tratamento da exceção end-of-file
...
}
catch(FileNotFoundException e) {
// código de tratamento da exceção file-not-found
...
}
catch(Exception e) { Exceção mais genérica
// código de tratamento da exceção genérica (Exception)
...
} 40
Tratamento de exceções
▸ Podem existir vários blocos catch de tratamento de exceções
▹ Multi-catch (a partir do Java 7)
▹ Permite usar o mesmo bloco de catch para mais do que um tipo de exceção:

try {
...
ref.process();
...
}
catch(EOFException |FileNotFoundException e){
// código de tratamento para ambos os tipos de exceção
...
}

41
Tratamento de exceções
▸ A clausula finally
▹ A clausula finally é um bloco de código que aparece depois do(s) bloco(s) catch e
que é executado sempre, exista ou não exista exceção

try {
// Instruções com verificação de ocorrência de exceção
}
catch(Exception e){
// Código do tratamento da exceção
}
finally{
// Ações comuns que devem ser executadas haja ou não haja exceção.
}

42
Exceções – Procura Ascendente de um Catch
▸ Quando o método que lança a exceção não tem um catch para a exceção lançada, a procura do bloco catch
adequado propaga-se pelos métodos clientes até se encontrar um catch para essa exceção ou se atingir o método
main e terminar o programa.

public void static main(int[] args) {


metodo1();
}
metodo1(){ Procura o catch para "e": Encontra
try {
metodo2(); e executa
}
// tem catch para exceção "e"
}
Procura o catch para "e":
metodo2(){
metodo3(); Não encontra vai procurar no método cliente
// não tem catch para exceção "e"
}

metodo3(){
// método onde ocorre ou que lança uma exceção "e"
}

43
Definição de
Exceções
▸ Exceções

44
Categorias de exceções
Object

Classes da Standard Library


Throwable
▸ Existem duas categorias de exceções:
▹ Exceções verificadas Exception Error
▹ Subclasses de Exception
▹ Usadas para falhas que se preveem RunTimeException

▹ A recuperação poderá ser possível


MinhasExceçõesVerificadas
▹ Exceções não verificadas

Classes do Utilizador
▹ Subclasses de RunTimeException
▹ Usadas para falhas que não são previstas
▹ A recuperação é pouco provável
MinhasExceçõesNãoVerificadas

45
Exceções verificadas
▸ As exceções não verificadas podem ou não ser capturadas e tratadas pela aplicação
▹ Se acontecerem e não forem tratadas a aplicação termina
▸ As exceções verificadas são feitas para serem capturadas e tratadas pela aplicação
▹ O compilador assegura que são capturadas dando erro de compilação se isso não acontecer.
▹ Estas exceções se forem convenientemente tratadas as falhas serão recuperáveis
▹ Os métodos que lançarem exceções verificadas devem incluir na assinatura do método a palavra reservada
throws seguida da exceção ou exceções que lançam (separadas por vírgulas)
▹ Exemplo:

public void saveToFile(String destinationFile) throws IOException {

// código do método

} IOException:
É uma exceção verificada do Java
que pode ser lançada neste método 46
Exceções – Definição de novas exceções
▸ Para definir novas exceções:
▹ Cria-se uma classe derivada de RuntimeException para criar uma nova exceção não verificada pelo compilador
▹ Cria-se uma classe derivada de Exception para uma exceção verificada pelo compilador.
▸ A definição de novas exceções oferece uma informação mais precisa do problema.

public class NoMatchingDetailsException extends Exception {


public NoMatchingDetailsException(String message){
super(message);
}
public String toString(){
return "No details matching the key were found.";
}
}
▹ No método servidor:
public ContactDetails getDetails(String key) {
if (key == null){
throw new NoMatchingDetailsException("null key in getDetails");
}
return book.get(key);
}
47
Exceções – Definição de novas exceções
public class NoMatchingDetailsException extends Exception {
public NoMatchingDetailsException(String message){
super(message);
}
public String toString(){
return "No details matching the key were found.";
}
}

▸ No método servidor: public ContactDetails getDetails(String key) {


if (key == null) {
throw new NoMatchingDetailsException("null key in getDetails");
}
return book.get(key);
}

▸ No método cliente: AddressBook book = new AddressBook();


try {
ContactDetails contact = book.getDetails(null);
System.out.println("Details: " + contact);
}
catch(Exception e) {
System.out.println("A chave deu problemas: " + e.getMessage());
}
48
Exceções – Definição de novas exceções
▸ A definição de novas exceções pode oferecer mais informação sobre o problema se forem adicionados atributos com essa informação.

Guarda a
public class NoMatchingDetailsException extends Exception {
informação sobre a
private String key; chave que originou
o erro
public NoMatchingDetailsException(String message, String key){
super(message);
this.key = key;
}
Vai obter-se a
public String getKey(){ key específica
que originou o
return key;
erro
}
public String toString(){
return "No details matching '" + key + "' were found.";
}
} 49
Exceções – Definição de Novas Exceções
▸ Neste exemplo adiciona-se no lançamento da exceção a informação especifica da exceção:
public ContactDetails getDetails(String key) throws NoMatchingDetailsException {
...
throw new NoMatchingDetailsException("Chave com problemas", key);
...
}

▸ No caso da exceção ser gerada, temos acesso, através da variável “e” (declarada como parâmetro do
catch), à chave que originou o erro:
...
try { Acesso à informação
... da key que originou
contact = agenda.getDetails("Maria"); o erro
...
}
catch (NoMatchingDetailsException e) {
System.out.println("A chave " + e.getKey() + " deu problemas");
...
} 50
Exceções – Recuperação de Erros
// Tenta guardar a agenda telefónica
▸ Os clientes devem tratar as boolean success = false;
int attempts = 0;
notificações de erro. do {

try {
Verifique os valores addressbook.saveToFile(fileName);
retornados. success = true;
}
▹ Não ignore as exceções. catch (IOException e) {
▸ Inclua código para tentar
System.out.println("Incapaz de guardar o
ficheiro " + nomeFicheiro );
recuperar do erro. attempts++;
if (attempts < MAX_TENTATIVAS) {
▹ Frequentemente isso implica fileName = alternativeName;
um ciclo. }
}
} while (!success && attempts < MAX_ATTEMPTS);
if (!success) {
//Relate o problema e desista;
}
51
Exceções – Mais Comuns
▸ ArithmeticException ▸ StringIndexOutOfBoundsException
Indica falhas no processamento aritmético, tal como uma Indica a tentativa de usar um índice numa string fora
divisão inteira por 0. dos seus limites

▸ ArrayIndexOutOfBoundsException ▸ NumberFormatException
Indica a tentativa de conversão de uma string para um
Indica a tentativa de acesso a um elemento de um array fora
formato numérico, mas que o seu conteúdo não
dos seus limites: ou o índice é negativo ou maior ou igual ao representava um número para aquele formato.
tamanho do array.
▸ NullPointerException
▸ IndexOutOfBoundsException Indica que a instrução tentou usar null onde era
Indica a tentativa de usar um índice fora dos limite de uma necessária uma referência a um objeto
tabela. ▸ IllegalArgumentException
▸ ArrayStoreException Quando o argumento do método tem um valor impossível
Indica a tentativa de armazenamento de um objeto inválido ▸ IOException
numa tabela. Indica a ocorrência de qualquer tipo de falha em
operações de entrada e saída.
▸ NegativeArraySizeException
Indica a tentativa de criar uma tabela com dimensão negativa.
52
Resumindo
▸ A linguagem Java permite o tratamento de situações de exceção de uma forma normalizada através da utilização de 5 palavras
chave correspondentes a cláusulas especiais, a saber:
▹throws public void method() throws ExcecaoVerificadaHerdaDeException {

▹throw
// […] implementação do método
throw new ExcecaoVerificadaHerdaDeException(...);

▹try // […] mais implementação do método (que não será executada


// em caso de ser lançada a exceção)
▹catch }
// Na chamada do método:
▹finally try{
// […] instruções com chamada ao metodo()
}
catch (ExcecaoVerificadaHerdaDeException e) {
// Tratamento da exceção "e" do tipo
// ExcecaoVerificadaHerdaDeException
}
catch (RuntimeException e) {
// Tratamento da exceção "e" do tipo RuntimeException
}
finally{
// Bloco opcional se existir é sempre executado
} 53
Resumindo
O mecanismo de exceções é a forma indicada em programação
orientada por objetos para lidar com os erros.

▹throws public void method() throws ExcecaoVerificadaHerdaDeException {


// […] implementação do método
▹throw throw new ExcecaoVerificadaHerdaDeException(...);
// […] mais implementação do método (que não será executada
▹try // em caso de ser lançada a exceção)

▹catch
}
// Na chamada do método:

▹finally try{
// […] instruções com chamada ao metodo()
}
catch (ExcecaoVerificadaHerdaDeException e) {
// Tratamento da exceção "e" do tipo
// ExcecaoVerificadaHerdaDeException
}
catch (RuntimeException e) {
// Tratamento da exceção "e" do tipo RuntimeException
}
finally{
// Bloco opcional se existir é sempre executado
} 54
Bibliografia
▸ Objects First with Java (6th
Edition), David Barnes & Michael
Kölling, Pearson Education
Limited, 2016
▹ Capítulo 14 (14.1 a 14.6)

55
Programação Orientada por Objetos

Entradas e
saídas

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Entrada e Saída de Dados


▸ Exemplo Prático

▸ Serialização

2
Entrada e Saída
de Dados
▸ Entradas e Saídas
Exemplo – Address Book
▸ Criar uma aplicação para guardar contactos.
▹ Cada contacto regista a informação do nome, telefone e endereço.
▹ Deve ser possível efetuar as operações habituais de criação, listagem,
alteração e remoção de contactos (operações CRUD).
▹ Deve existir uma forma de procurar contactos pelo nome ou telefone.
▹ Criar uma interface de consola para a aplicação.
▹ Guardar os contactos e o resultado das procuras em ficheiro

4
Exemplo – AddressBook
▸ Diagrama de classes da aplicação Address Book:

5
Exemplo – AddressBook
▸ Classes da aplicação
AddressBook:
▹ ContactDetails – Informação
do contacto.
▹ AddressBook – Lista de
contactos.
▹ AddressBookDemo – Cria um
livro de contactos com alguns dados
para testes.
▹ AddressBookFileHandler –
é responsável pela leitura e escrita
em ficheiro da lista de contactos e
dos resultados de procura dos
contactos.
6
Exemplo – AddressBook
▸ Classe AddressBookDemo
public class AddressBookDemo {
private AddressBook book;
public AddressBookDemo() {
ContactDetails[] sampleDetails = {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
}
public AddressBook getBook() { Cria um AddressBook
return book; com uma lista inicial de
} contactos
} 7
Exemplo – AddressBook
▸ Classe AddressBookFileHandler
Guarda a referência do
livro de contactos que irá
public class AddressBookFileHandler {
ser utilizado na escrita e
private AddressBook book; na leitura para ficheiro
private static final String RESULTS_FILE = "results.txt";

Nome do ficheiro
public AddressBookFileHandler(AddressBook book) { que irá guardar o
resultado da última
this.book = book;
procura
}
Recebe o livro de
// Continua… contactos no
construtor

Ø Antes de vermos a implementação será necessário perceber como funciona a escrita e leitura de ficheiros …

8
Entradas e saídas
Ø Em Java os ficheiros e as pastas (ou diretórios) são representados pela classe File
Ø Importa-se como java.io.File
Ø Em java 7 foram acrescentadas a interface Path e as classes Files e Paths importadas de
java.nio.file dedicadas igualmente à manipulação de ficheiros.

▹ CONSTRUTORES
Classe File:
File(String caminho) construtor de directórios/ficheiros
File(String caminho&filename) construtor com caminho e nome do ficheiro

▹ MÉTODOS
boolean canRead() ficheiro/directório pode ser lido
boolean canWrite() pode-se gravar no ficheiro/directório
boolean delete() apaga ficheiro/directório
boolean exists() verifica se ficheiro/directório existem
boolean isAbsolute() verifica se caminho é absoluto
boolean isDirectory() verifica se objecto é directório
boolean isFile() verifica se objecto é ficheiro
boolean mkdir() cria directório do objecto
boolean mkdirs() cria directórios do caminho
boolean renameTo(String novo) muda nome do ficheiro/directório para novo
9
Entradas e saídas
▸ Exemplo de utilização da classe File

import java.io.File; Cria a representação do


ficheiro:
class FileDemo { O objeto File
public static void main(String[] args) {
String filename = "dados.txt";
File file = new File(filename); Verifica se o
if (file.exists()) { ficheiro existe
System.out.println(file.getName() + " existente");
}
else {
System.out.println(file.getName() + " não existente");
}
}
}

10
Entradas e saídas
▸ Para além da representação dos ficheiros o Java inclui classes dedicadas à escrita e à leitura
dos ficheiros em disco.
▹ Existem classes independentes para a escrita e para a leitura de ficheiros.

▸ De acordo com o tipo de informação que é armazenada, os ficheiros podem ser classificados
como ficheiros de texto ou ficheiros binários
▹ Os ficheiros de texto guardam caracteres e podem ser lidos e editados por qualquer aplicação de edição de texto
(notepad, word, etc.)
▹ Na realidade a informação é guardada em bytes que representam caracteres de acordo com um determinado standard
(ASCII, Unicode, etc.)
▹ Neste caso existe alguma interpretação tanto na leitura como na escrita destes ficheiros de texto
▹ Os ficheiros binários guardam a informação em bytes que não é possível interpretar sem a ferramenta
adequada. Por exemplo imagens, música, vídeo, etc.
▹ Neste caso não existe qualquer interpretação dos bytes escritos ou lidos.

11
Entradas e saídas
▸ Para a leitura e escrita de ficheiros de texto existem várias classes que formam uma hierarquia e
que derivam respetivamente das classes abstratas Reader e Writer.

12
Entradas e saídas
▸ Para a leitura e escrita de ficheiros binários existem várias classes que formam uma hierarquia e que
derivam respetivamente das classes abstratas InputStream e OutputStream.

13
Entradas e saídas
▸ A leitura e escrita de ficheiros independentemente de serem de texto ou binários é
feita sequencialmente.
▸ Os processos de escrita ou de leitura de ficheiros são sempre feitos em
3 etapas:
1. Abrir o ficheiro
2. Operação de escrita ou de leitura
3. Fechar o ficheiro.
▸ As operações com ficheiros estão também sujeitas a muitos tipos de falhas, por isso a
maior parte delas pode levantar exceções, que são sempre verificadas (obrigam à
utilização de blocos try-catch)

14
Entradas e saídas
▸ Hierarquia de exceções de io (input/output = entrada/saída) do Java

15
Ficheiros de texto
▸ Exemplo da escrita de um ficheiro de Texto usando a classe FileWriter.

public class TesteFileWriter {


1. O ficheiro com que se vai trabalhar

public static void main(String arg[]) {


File ficheiro = new File("textOutput.txt"); 2. Criação das classes envolvidas na escrita
try { FileWriter fileWriter = new FileWriter(ficheiro);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
PrintWriter printWriter = new PrintWriter(bufferedWriter);
printWriter.println("Saida c/ PrintWriter. Tipos primitivos conv. em strings ");
boolean aBoolean = false;
int anInt = 1234567;
printWriter.println(aBoolean); 3. Escreve-se como se fosse para o ecrã
printWriter.println(anInt);
printWriter.flush();
4. Forçar a escrita (flush) e fechar o ficheiro
printWriter.close();
}
catch (IOException e) { System.out.println(e.getMessage());
}
}
}
16
Ficheiros de texto
▸ Exemplo da leitura de um ficheiro de Texto usando a classe FileReader.
1. O ficheiro com que se vai trabalhar
public class TesteFileReader {
public static void main(String arg[]) {
File ficheiro = new File("textOutput.txt"); 2. Criação das classes envolvidas na leitura
try {
FileReader fileReader = new FileReader(ficheiro);
BufferedReader bufferedReader = new
BufferedReader(fileReader);
String line = "";
while (line != null) { 3. Lê-se cada linha para uma string
line = bufferedReader.readLine();
System.out.println(line);
}
4. Fechar o ficheiro
bufferedReader.Close();
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
17
Ficheiros de texto
▸ Exemplo da leitura de um ficheiro de Texto usando a classe Scanner.
public class TesteLeituraComScanner { 1. O ficheiro com que se vai trabalhar
public static void main(String arg[]) {
File file = new File("textOutput.txt");
try {
Scanner scanner = new Scanner(file);
// 3. Ler a informação do ficheiro. 2. Criação do objeto Scanner a partir do ficheiro (file)
String primeiraLinha = scanner.nextLine();
System.out.println("String lida: " + primeiraLinha);
boolean segundaLinha = scanner.nextBoolean();
3. Lê-se como foi explicado antes
System.out.println("Boolean lido: " + segundaLinha);
int terceiraLinha = scanner.nextInt();
System.out.println("Inteiro lido: " + terceiraLinha);
}
catch (InputMismatchException e) {
System.out.println("Mismatch exception:" + e);
}
catch (FileNotFoundException e) {
System.out.println("Ficheiro não encontrado!");
System.exit(0);
}
}
} 18
Exemplo Prático

▸ Entradas e Saídas de Dados

19
Exemplo – Address Book
▸ Criar uma aplicação para guardar contactos.
▹ Cada contacto regista a informação do nome, telefone e endereço.
▹ Deve ser possível efetuar as operações habituais de criação, listagem,
alteração e remoção de contactos (operações CRUD).
▹ Deve existir uma forma de procurar contactos pelo nome ou telefone.

▹ Criar uma interface de consola para a aplicação.

▹ Guardar os contactos e o resultado das procuras em ficheiro

20
Exemplo – AddressBook
▸ Classes da aplicação AddressBook:
▹ ContactDetails – Informação do contacto.
▹ AddressBook – Lista de contactos.
▹ AddressBookDemo – Cria um livro de contactos
com alguns dados para testes.
▹ AddressBookFileHandler – é responsável pela
leitura e escrita em ficheiro da lista de contactos
e dos resultados de procura dos contactos.

21
Exemplo – AddressBook
▸ Classe AddressBookFileHandler
Guarda a referência do livro
de contactos que irá ser
public class AddressBookFileHandler { utilizado na escrita e na
leitura para ficheiro
private AddressBook book;
private static final String RESULTS_FILE = "results.txt";

public AddressBookFileHandler(AddressBook book) {


this.book = book;
} Nome do ficheiro
que irá guardar o
// Continua… resultado da última
procura
Recebe o livro de
contactos no
construtor

22
Exemplo – AddressBook
▸ Classe AddressBookFileHandler – métodos makeAbsoluteFilename e getProjectFolder

private File makeAbsoluteFilename(String filename) throws IOException {


try {
File file = new File(filename);
if(!file.isAbsolute()) { Cria e devolve a
file = new File(getProjectFolder(), filename); representação do ficheiro
} no diretório atual do
return file; projeto
}
catch(URISyntaxException e) {
throw new IOException("Unable to make a valid filename for " + filename);
}
}

private File getProjectFolder() throws URISyntaxException {


String myClassFile = getClass().getName() + ".class"; Obtém o nome do
URL url = getClass().getResource(myClassFile); diretório atual do
return new File(url.toURI()).getParentFile(); projeto
}
23
Exemplo – AddressBook
▸ Classe AddressBookFileHandler – método saveSearchResults

public void saveSearchResults(String keyPrefix) throws IOException {


File resultsFile = makeAbsoluteFilename(RESULTS_FILE);
ContactDetails[] results = book.search(keyPrefix); Faz uma procura e
guarda os resultados
FileWriter writer = new FileWriter(resultsFile); num array de
for(ContactDetails details : results) { contactos: results

writer.write(details.toString());
Escreve para disco
writer.write(‘\n’);
os vários contactos
writer.write(‘\n’); do array results
}
writer.close();
Este método terá de ser
} chamado dentro de um
bloco try-catch
24
public void showSearchResults() {
BufferedReader reader = null;

Exemplo –
try {
File resultsFile = makeAbsoluteFilename(RESULTS_FILE);
reader = new BufferedReader(new FileReader(resultsFile));

AddressBook
System.out.println("Results ...");
String line; line = reader.readLine();
while(line != null) {


System.out.println(line);
Classe line = reader.readLine();
AddressBookFileHandler }
System.out.println();
– método }
showSearchResults catch(FileNotFoundException e) {
System.out.println("Unable to find the file: " + RESULTS_FILE);
}
catch(IOException e) {
System.out.println("Error encountered reading the file: " +
RESULTS_FILE);
}
finally {
Lê do ficheiro os if(reader != null) {
resultados da busca try { reader.close();
e mostra-os no ecrã }
catch(IOException e) {
System.out.println("Error on closing: " + RESULTS_FILE);
}
}
}
}

25
Exemplo – AddressBook
▸ Classe AddressBookFileHandler – método addEntriesFromFile

public void addEntriesFromFile(String filename) throws IOException {


URL resource = getClass().getResource(filename);
if(resource == null) {
throw new FileNotFoundException(filename);
}
filename = resource.getFile();
BufferedReader reader = new BufferedReader( new FileReader(filename));
String name;
name = reader.readLine();
while(name != null) { Lê um ficheiro de
String phone = reader.readLine(); texto com contactos
String address = reader.readLine(); com a informação do
// Discard the separating blank line. nome, telefone e
reader.readLine(); endereço separada
book.addDetails(new ContactDetails(name, phone, address)); por linhas
name = reader.readLine();
}
reader.close();
} 26
Exemplo – AddressBook
▸ Classe AddressBookFileHandler – método saveToFile

public void saveToFile(String destinationFile) throws IOException {


File destination = makeAbsoluteFilename(destinationFile);
ObjectOutputStream os = new ObjectOutputStream( new FileOutputStream(destination));
os.writeObject(book);
os.close();
} Escreve num ficheiro
binário a informação
dos contactos

▸ Neste caso a escrita para ficheiro no modo binário é feita utilizando a classe ObjectOutputStream
▹ O procedimento é semelhante à escrita em modo de texto mas as classes envolvidas são
diferentes e também estamos a escrever um objeto completo para o disco.
▹ É necessário igualmente usar este método dentro de um bloco try-catch

27
Exemplo – AddressBook
▸ Classe AddressBookFileHandler – método readFromFile

public AddressBook readFromFile(String sourceFile) throws IOException, ClassNotFoundException {


URL resource = getClass().getResource(sourceFile);
if(resource == null) {
throw new FileNotFoundException(sourceFile);
Lê de um ficheiro
}
binário a informação
try { dos contactos
File source = new File(resource.toURI());
ObjectInputStream is = new ObjectInputStream( new FileInputStream(source));
AddressBook savedBook = (AddressBook) is.readObject();
is.close();
return savedBook;
}
catch(URISyntaxException e) {
throw new IOException("Unable to make a valid filename for " + sourceFile);
}
}

Efetua a leitura do ficheiro binário dos contactos escritos anteriormente.


28
Serialização
▸ Entradas e Saídas de Dados

29
Serialização
▸ Além das formas tradicionais de escrita e leitura para ficheiro existe ainda uma outra forma
chamada serialização de objetos
▹ Neste caso é escrito em modo binário para o ficheiro um objeto de uma determinada classe incluindo os
objetos que são referenciados nos seus atributos
▸ O processo de leitura é chamado desserialização de objetos
▹ Agora são restaurados a partir do ficheiro os objetos que anteriormente foram serializados pela mesma
ordem em que foram guardados

▸ Em Java o algoritmo de serialização de dados garante:


▹ Que quando os dados venham a ser lidos de um ficheiro serializado, todos os objetos com
os respetivos atributos serão reconstruídos no estado em que estavam aquando da sua
gravação.

30
Serialização
▸ A serialização é aplicável apenas a instâncias de classes que implementem a interface
Serializable

public class Date implements Serializable {


private int year;
private int month;
private int day;
...
}

31
Serialização
▸ Ao declarar que uma classe implementa a interface Serializable, o compilador gera dois métodos privados para essa
classe:
▹ void writeObject ( ObjectOutputStream out ) throws IOException
▹ Object readObject ( ObjectInputStream in ) throws IOException, ClassNotFoundException

n Um objeto “ObjectOutputStream” representa um canal binário que trabalha diretamente sobre um ficheiro e
que armazena objetos e valores simples, usando o método writeObject() o qual implementa um algoritmo
de serialização (serialize).
n Um objeto “ObjectInputStream” representa um canal especial que trabalha diretamente sobre um
ficheiro e lê objetos e valores simples, usando o método readObject() o qual implementa um algoritmo
de desserialização (deserialize).

32
Serialização – Gravação em Ficheiro
▸ Considerando que
▹ A classe Persons inclui um array de objetos da classe Person
▹ E que a classe Person possui um atributo yearOfBirth da classe Date
▹ O método abaixo vai gravar num ficheiro binário
▹ um objeto da classe Persons,
▹ com todos os elementos do array de objetos (com todos os objetos da classe Person)
▹ e para cada objeto da classe Person, o nome e a respectiva data de nascimento
▹ Num ficheiro binário serializado a partir do qual será possível reconstituir completamente o objeto
da classe Persons
public static void saveFile(Persons listOfPersons, String filename) {
try {
ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(filename));
oos.writeObject(listOfPersons);
oos.flush(); oos.close();
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
33
Serialização – Leitura de Ficheiro
▸ Considerando que
▹ Foi gravado através de uma ObjectOutputStream num qualquer ficheiro binário, um objeto da classe
Persons
▹ É possível lê-lo do ficheiro reconstituindo completamente o seu estado no momento da gravação através do
método abaixo:
public static Persons readSerializedFile(String filename) {
Persons listOfPersons;
try {
ObjectInputStream ois = new ObjectInputStream( new FileInputStream(filename));
listOfPersons = (Persons) ois.readObject();
ois.close();
}
catch (IOException e) {
System.out.println(e.getMessage());
listOfPersons = new Persons(10);
}
catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
listOfPersons = new Persons(10);
}
return listOfPersons;
}
34
Serialização – modificador transient
▸ Na serialização o Java escreve no ficheiro todos os atributos, não static, da classe que implementa a interface
java.io.Serializable.
▸ Podemos indicar que não pretendemos que um atributo seja escrito (eventualmente porque o seu tipo é de uma
classe que não implementa a interface Serializable) desde que utilizemos o modificador transient:
▹ private transient Color cor; //Color não é Serializable
▸ Se for importante a informação do atributo teremos que implementar as nossas versões dos métodos que fazem a
escrita e leitura da informação:
▹ private void writeObject(java.io.ObjectOutputStream oos) throws IOException
▹ private void readObject(ObjectInputStream ois) throws ClassNotFoundException,
IOException
▹ Estes métodos devem chamar, normalmente no seu início, o comportamento por omissão:
▹ oos.defaultWriteObject();
▹ ois.defaultReadObject();

35
Bibliografia

▸ Objects First with Java (6th Edition),


David Barnes & Michael Kölling,
Pearson Education Limited, 2016
▹ Capítulo 14 (14.9 e 14.10)

36
Programação Orientada por Objetos

JavaFX – Eventos
e Painéis

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário
▸ Programação por eventos
▸ Classe Event
▹ Ações, Eventos e Resultados das ações
▹ Um nó que reaja a diferentes ações
▹ Exemplo
▸ Paineis
▹ BorderPane
▹ GridPane
▹ HBox e Vbox
▹ Painéis compostos por painéis
▸ Exemplo de Eventos e Paineis

2
Programação por
Eventos
▸ Eventos e Paineis

3
JavaFX - Eventos
1. Ação: quando o

Na programação baseada em eventos o utilizador prime o
código é executado quando ocorre o botão …
acontecimento (evento) 2. Evento: é gerado um
▹Por exemplo, podemos querer que o evento …
programa execute uma operação em 3. Executa Código: se o
resposta a ações do utilizador tais como: programador
▹Premir um botão do rato associou código ao
▹Deslocar o rato evento gerado esse
código é executado.
▹Premir um botão
▹Premir uma tecla

▹Ou em resposta a outros acontecimentos


como a passagem do tempo (gerida por
temporizadores)

4
JavaFX – Eventos
▸ A classe Event public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
javafx.event.Event;
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>(){
▸Podemos olhar para um evento (acontecimento) como @Override
sendo um sinal, representado por um objeto, enviado public void handle(ActionEvent event) {
ao programa, assinalando a ocorrência de uma ação. System.out.println("Hello World!");
▹Geralmente os eventos ocorrem na sequência de }
});
ações do utilizador (mas nem sempre)
▹O programa pode reagir ao evento ou ignorá-lo.
StackPane root = new StackPane();
root.getChildren().add(btn);
▹Um evento é originado por um objeto fonte, que primaryStage.setScene(new Scene(root, 300, 250));
pode ser conhecido através do método: primaryStage.show(); }
Object getSource()

Dentro do método handle seria possível aceder ao botão, que gerou o evento, através de:

Button botao = (Button)event.getSource();


5
JavaFX - Eventos
▸ O método setOnAction, da classe Button, recebe como argumento um objeto
EventHandler<ActionEvent>:
public final void setOnAction(EventHandler<ActionEvent> value)
▸ EventHandler é uma interface, genérica, que implementa o método handle:

public interface EventHandler<T extends Event> extends EventListener {


public void handle(T event);
}

▸ O método handle recebe como argumento um objeto da classe Event, ou respetivas subclasses, que
contém informação sobre o evento (ex. getSource() devolve o objeto onde foi gerado o evento)
▸ Na abordagem tradicional teríamos que criar uma classe que implementasse a interface
EventHandler<ActionEvent> (fazendo o override do método handle) e passar um objeto dessa
classe como argumento para o setOnAction.

6
JavaFX - Eventos
import javafx.event.ActionEvent;
import javafx.event.EventHandler;

public class ButtonAction implements EventHandler<ActionEvent> {


private String message;
public ButtonAction(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void handle(ActionEvent event) {
System.out.println(message);
}
}

//...
btn.setOnAction(new ButtonAction("Hello World!"));
//...
7
JavaFX – Eventos (classe anónima)
▸ Para não se criar uma classe específica para cada ação associada a cada evento, é possível
recorrer à criação de classes anónimas (estender uma classe já existente ou implementar uma
interface) :
▹ http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html

▸ Com a utilização de uma classe anónima é possível declarar e instanciar uma classe num único
passo.
▸ Na definição de uma classe anónima utiliza-se o operador new, seguido do nome da interface
(ou da classe que se pretenda estender), seguido de eventuais argumentos do construtor
(apenas () quando se trata de uma implementação de interface), seguido do corpo da classe:
new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
} 8
JavaFX - Eventos
▸ Como associar ações do utilizador a nós do grafo de cena?
1. Definir o evento a capturar na respetiva propriedade do nó em causa. public void start(Stage primaryStage) {
Exemplo para o nó btn: primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setOnAction(eventHandler) btn.setText("Say 'Hello World'");
btn.setOnAction( new EventHandler<ActionEvent>() {
2. Associar a essa propriedade o (novo) tratador de eventos (definido em @Override
função do tipo de evento que pretendemos capturar): public void handle(ActionEvent event) {
System.out.println("Hello World!");
EventHandler<EventoACapturar>()
}
3. Redefinir o método handle do tratador de eventos passado, colocando
});
StackPane root = new StackPane();
nesse método o código a ser executado root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
public void handle (EventoACapturar event) primaryStage.show();
}

9
JavaFX – Eventos (expressões lambda)
▸ O uso de classes anónimas veio simplificar bastante a indicação do código a executar (método
handle) quando o evento é gerado.
▸ No Java 8 é possível simplificar a utilização de implementações de interfaces anónimas que
apenas possuem um método (as chamadas interfaces funcionais), através da utilização do
conceito de expressão lambda: http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
▸ Uma expressão lambda (o seu nome advém do estudo “λ-calculus” realizado pelo matemático
Alonzo Church na década de 1930) permite representar um método indicando apenas o(s) seu(s)
parâmetro(s) e o seu corpo (omitindo o seu nome).
▸ Assim, o anterior método handle poderia ser representado por:

(ActionEvent event) -> { System.out.println("Hello World!"); }


10
JavaFX – Eventos (expressões lambda)
▸ Nas expressões lambdas pode ser omitido o tipo do parâmetro (o compilador consegue determiná-lo em função
da interface onde o método foi definido), assim (ActionEvent event) poderá apenas ser representado por
(event).
▸ Quando o método apenas possui um parâmetro é, ainda, possível eliminar os parêntesis (estes são importantes
quando existem diversos parâmetros, que serão separados por virgulas). Nesta situação é comum utilizar apenas
uma letra para designar o parâmetro:
e -> { System.out.println("Hello World!"); }
▸ Analogamente, se o corpo apenas possuir uma instrução, as chavetas podem ser omitidas, bem como o ponto e
vírgula terminador:
e -> System.out.println("Hello World!")
▸ Se a instrução for um return, este poderá ser omitido. Um método da forma

int sum (int a, int b) {

return a + b;
} poderia ser representado por (a, b) -> a + b
11
JavaFX – Eventos
▸ Em resumo, em Java 8, a associação do método do evento pode ser feita:
▹ Através da criação de uma classe específica que implementa a interface do evento:
public class ActionButton implements EventHandler<ActionEvent> {
private String message;
public ActionButton(String message) { this.message = message; }
public String getMessage() {return message; }
public void setMessage(String message) {this.message = message;}
@Override
public void handle(ActionEvent event) { System.out.println(message); }
}

btn.setOnAction(new ActionButton("Hello World!"));


▹ Através da criação de uma classe anónima diretamente na associação do evento:

btn.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent event){
System.out.println("Hello World!"); } });

▹ Através da utilização de uma expressão lambda:


btn.setOnAction(e -> System.out.println("Hello World!"));
12
JavaFX - Eventos public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");

▸ Associar múltiplas ações do utilizador a um nó do grafo de btn.setOnMouseClicked(new EventHandler<MouseEvent>(){


cena @Override
public void handle(MouseEvent event) {
System.out.println("Premi o botão!");
1. Definir o evento a capturar nas respetivas propriedades do nó : }
});

btn.setOnMouseClicked( btn.setOnMouseExited(new EventHandler<MouseEvent>(){


new EventHandler <MouseEvent>() ... ) @Override
public void handle(MouseEvent event) {
System.out.println("Saí da área do botão");
}
btn.setOnMouseExited(
});
new EventHandler <MouseEvent>() ... )
StackPane root = new StackPane();

2. Para cada uma das propriedades proceder com uma das


root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
alternativas do slide anterior primaryStage.show();
}

13
Paineis
▸ Eventos e Paineis

14
JavaFX – Gestores de Painéis
▸ Até ao momento, temos inserido os nós folha diretamente nas janelas ou no painel StackPane, sem
nos preocuparmos em definir as suas posições (absolutas ou relativas).
▸ Em aplicações reais isso não é aceitável e temos de especificar as posições dos componentes por
forma a produzir uma interface gráfica do utilizador (GUI) facilmente compreensível.
▸ Duas abordagens para inserir componentes gráficos:
▹ Rígida: especificar onde os componentes deverão ser colocados, através de coordenadas
absolutas
▹ Usada apenas na prototipagem rápida de aplicações
▹ A apresentação final fica dependente da plataforma
▹ Não suporta adequadamente o redimensionamento
do container (janela ou painel)
▹ Flexível: utilização de painéis
▹ Adapta a posição dos componentes gráficos
a possíveis redimensionamentos da janela
▹ Apresentação final adapta-se à plataforma utilizada
15
JavaFX – Gestores de Painéis
▸ O package layout - javafx.scene.layout.*;
▹ O JavaFX disponibiliza no package layout vários modelos pré-definidos para disposição de componentes.
▹ Os componentes que estabelecem e gerem os diferentes modelos de disposição de elementos são denominados
painéis (Pane).
▹ Os elementos gráficos são adicionados a esses painéis e são dispostos de acordo com o modelo definido por
esse painel.
▹ Vamos ver apenas quatro dos mais comuns.

n BorderPane n GridPane n VBox n HBox

16
JavaFX – Gestores de Painéis

▸ BorderPane

javafx.scene.layout.BorderPane;

▸O BorderPane possui cinco zonas para colocação de um


elemento gráfico.
▹O elemento é colocado numa posição dentro da zona
definida pelo próprio BorderPane.
▹Cada zona só pode conter um componente, mas este
pode ser outro painel.

17
import javafx.scene.layout.BorderPane;
JavaFX – Gestores de Painéis
public class BorderPaneExample extends Application {

@Override
public void start(Stage primaryStage) {

Button btn1 = new Button("Botao 1");


Button btn2 = new Button("Botao 2");
Button btn3 = new Button("Botao 3");
Button btn4 = new Button("Botao 4");
Button btn5 = new Button("Botao 5");
BorderPane root = new BorderPane();
root.setTop(btn1);
root.setLeft(btn2);
root.setRight(btn3);
root.setBottom(btn4);
root.setCenter(btn5);
primaryStage.setTitle("Border Layout");
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}

public static void main(String[] args) {


launch(args);
}
}
18
JavaFX – Gestores de Painéis
public void start(Stage primaryStage) {
primaryStage.setTitle("Grid Layout");
▸ GridPane Button btn1 = new Button("Botao 1");
Button btn2 = new Button("Botao 2");
Button btn3 = new Button("Botao 3");
javafx.scene.layout.GridPane; Button btn4 = new Button("Botao 4");
Button btn5 = new Button("Botao 5");
▸Um GridPane define uma grelha (tabela) . GridPane root = new GridPane();
root.setHgap(20);
▹Os elementos gráficos são adicionados a uma célula root.setVgap(20);
especifica da tabela. root.setPadding(new Insets(20, 10, 20, 10));
root.setGridLinesVisible(true);
▹É possível definir o espaçamento entre células. root.add(btn1,0,1);
▹É possível determinar se as linhas da grelha são ou não root.add(btn2,0,2);
root.add(btn3,0,3);
visíveis. root.add(btn4,1,0);
root.add(btn5,1,1);
primaryStage.setScene(new Scene(root,300,250));
primaryStage.show();
}

19
JavaFX – Gestores de Painéis Um painel VBox
distribui os
componentes na
▸ HBox e VBox vertical

javafx.scene.layout.HBox;
javafx.scene.layout.VBox; public void start(Stage primaryStage) {
primaryStage.setTitle("BOXs Layout");
Button btn1 = new Button("Botao 1");

▸ Um painel HBox distribui os Button btn2 = new Button("Botao 2");


Button btn3 = new Button("Botao 3");
Button btn4 = new Button("Botao 4");
componentes na horizontal Button btn5 = new Button("Botao 5");
HBox root = new HBox();
root.setPadding(new Insets(15,12,15,12));
root.setSpacing(10);
root.getChildren().addAll(btn1, btn2, btn3, btn4, btn5);
primaryStage.setScene(new Scene(root, 500, 250));
primaryStage.show();
}

20
JavaFX – Gestores de Painéis
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
HBox hbox = addBotoes();
root.setTop(hbox);
VBox vbox = addTextos();
▸ Painéis Compostos root.setCenter(vbox);
Scene scene = new Scene(root, 700, 500);
▹ Objetivo: criar um BorderPane primaryStage.setScene(scene);
primaryStage.show();

com um HBox no topo e um }


private HBox addBotoes() {
contentor VBox no centro. HBox hbox = new HBox();
hbox.setPadding(new Insets(15,12,15,12));
▹ Criar painel do tipo BorderPane hbox.setSpacing(10);
hbox.setStyle("-fx-background-color: #336699;");
▹ Criar e adicionar um painel do tipo Hbox addButtons(hbox);
▹ Criar e adicionar um painel do tipo Vbox }
return hbox;

▹ Definimos o HBox e adicionamos botões ao private VBox addTextos() {


VBox vbox = new VBox();
Hbox vbox.setPadding(new Insets(10));
▹ Definimos um VBox e adicionamos texto. vbox.setSpacing(10);
addTexts(vbox);
return vbox;
}

21
JavaFX – Gestores de Painéis
▸ Painéis Compostos
▹ Objetivo: criar um BorderPane com um
HBox no topo e um VBox no centro.
▹ Estrutura de nós do grafo de cena:

1. Root (BorderPane)
1.1 Top – HBox – (área de botões)
1. Botão Vermelho
2. Botão Azul
1.2 Center - VBox – (área do texto)
1. Text ("Font Serif")
2. Text ("Font SanSerif")
3. Text ("Font Monospaced")

22
Exemplo de
Eventos e Paineis
▸ Eventos e Paineis

23
JavaFX- Eventos : Exemplo
▸ Objetivo: Construir uma aplicação com a interface gráfica onde os textos mudam
de vermelho para azul e vice-versa em função do botão acionado.

24
JavaFX- Eventos : Exemplo

Objetivo
Criar a estrutura principal da aplicação.
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
1. Criar um BorderPane
HBox hbox = addButtons();
2. Adicionar os Botões root.setTop(hbox);
3. Adicionar os Textos VBox vbox = addTexts();
root.setCenter(vbox);
4. Criar a Cena Scene scene = new Scene(root, 700, 500);
5. Associar a Cena ao Stage primaryStage.setScene(scene);
primaryStage.show();
6. Mostrar }

25
JavaFX- Eventos : Exemplo

Objetivo private HBox addButtons() {


HBox hbox = new HBox();
hbox.setPadding(new Insets(15, 12, 15, 12));
Criar o painel com os botões (será colocado no hbox.setSpacing(10);
topo). hbox.setStyle("-fx-background-color: #336699;");
addButtons(hbox);
1. Criar um painel HBox, definindo as suas }
return hbox;

características private void addButtons(HBox hbox) {


hbox.getChildren().add(createButton("Vermelho", Color.RED));
2. Adicionar os Botões como filhos do painel hbox.getChildren().add(createButton("Azul", Color.BLUE));
}
3. Na criação do botão é definido o seu rótulo e a private Button createButton(String text, final Color color) {
ação a executar em caso de seleção (acesso a Button btn = new Button(text);
"variável externa") btn.setOnAction(e -> changeColor(color, e));
return btn;
}

26
JavaFX- Eventos : Exemplo private VBox addTexts() {
VBox vbox = new VBox();
vbox.setPadding(new Insets(10));
Objetivo vbox.setSpacing(10);
addTexts(vbox);
return vbox;
Criar o painel com os textos (será colocado no }
centro).
private void addTexts(VBox vbox) {
1. Criar um painel VBox, definindo as suas vbox.getChildren().addAll(
características createText("Font Serif","Serif",30),
createText("Font SanSerif","SanSerif",50),
2. Adicionar os Textos como filhos do painel
);
createText("Font Monospaced","Monospaced",70)

3. Na criação do texto é indicado o seu conteúdo, o


}

nome da fonte e o respetivo tamanho private Text createText(String sentence,


String fonteName,
int size) {
Text text = new Text(sentence);
Font font = Font.font(fonteName, size);
text.setFont(font);
return text;
}

27
JavaFX- Eventos : Exemplo
Objetivo: Ação a executar pelos botões.
1. Obter o botão que gerou o evento (getSource())
2. Seguir a hierarquia para obter o painel principal
3. Obter o painel (VBox) que tem os textos
4. Percorrer todos os nós do painel e, caso sejam textos, modificar a sua cor

private void changeColor(Color color, ActionEvent event) {


Button bnt = (Button)event.getSource();
BorderPane root = (BorderPane)bnt.getParent().getParent();
VBox vbox = (VBox)root.getCenter();
Text text;
for (Node no : vbox.getChildrenUnmodifiable()) {
if (no instanceof Text) {
text = (Text)no;
text.setFill(color);
}
}
}

28
JavaFX- Eventos : Exemplo

29
Resumindo
▸ Eventos
▹ A Programação baseada em eventos permite interagir com uma interface gráfica
▹ A Classe Event
▹ Uma ação do utilizador sobre um componente do GUI
▹ Faz com que esse objeto gere um evento
▹ Evento esse que, ao ser apanhado pelo “handler” apropriado, despoleta a execução do código
desse “handler”
▹ Para que um qualquer componente do GUI (nó) reaja a diferentes ações do utilizador basta
criar um “handler” para cada uma dessas ações no respetivo componente.

▸ Painéis (Pane)
▹ BorderPane (cinco zonas: top, bottom, left, right, center)
▹ GridPane (uma grelha)
▹ HBox e VBox (dispõem os componentes horizontal e verticalmente)
▹ Painéis compostos por painéis (podemos sempre inserir painéis em painéis)

30
Leitura Complementar
▸ Chapter 3 – Lambdas and Properties Pgs 61 a 73
▸ Chapter 4 – Layouts and UI Controls Pgs 91 a 101

▸ Documentação:
▹ http://docs.oracle.com/javase/8/javafx/api/toc.htm
▸ Eventos:
▹ http://docs.oracle.com/javase/8/javafx/api/javafx/event/package-summary.html
▹ http://docs.oracle.com/javase/8/javafx/events-tutorial/events.htm
▸ Painéis
▹ http://docs.oracle.com/javase/8/javafx/api/javafx/scene/layout/package-
frame.html
▹ http://docs.oracle.com/javase/8/javafx/layout-tutorial/index.html

31
Programação Orientada por Objetos

JavaFX – Controlos

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário
▸ Controlos do JavaFX
▹ Controlos
▹ Button – criação e utilização
▹ TextField – criação e utilização
▹ Label - – criação e utilização
▹ ListView – criação, utilização e preenchimento
▸ Exemplo Controlos simples
▸ Exemplo: Desenhador de Formas

2
CoNtrolos do JavaFX

▸ JavaFX – Controlos
JavaFX – Controlos
▸ As classes para criar controlos de interface
com o utilizador encontram-se no pacote
javafx.scene.control

▸ Exemplos:
▹ Button
▹ TextField
▹ Label
▹ ListView

4
JavaFX – Controlos
public void start(Stage primaryStage) {
▹ No exemplo inicial: primaryStage.setTitle("Hello World!");
Button btn = new Button();
▹ Definiu-se um botão! btn.setText("Say 'Hello World'");
btn.setOnAction(
new EventHandler<ActionEvent>(){
@Override
Button btn = new Button(); public void handle(ActionEvent event) {
btn.setText("Say 'Hello World'"); System.out.println("Hello World!");
}
});


StackPane root = new StackPane();
Associou-se ao botão um handler para o root.getChildren().add(btn);
evento Action primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}

btn.setOnAction( //[…] );

5
JavaFX – Controlos
public void start(Stage primaryStage) {
primaryStage.setTitle("Botão Imagem");
▸ Colocar uma imagem num botão Image imageOk = new Image(getClass().getResourceAsStream(
"icons/ok.png"), 40, 40, false, false);
Button btn = new Button("OK", new ImageView(imageOk));

1. Criar uma imagem, associada a um ficheiro. btn.setOnMouseClicked(e -> System.out.println("OK"));

2. Associar a imagem ao botão. StackPane root = new StackPane();


root.getChildren().add(btn);

Scene scene = new Scene(root, 300, 250);

primaryStage.setScene(scene);
primaryStage.show();
}

6
JavaFX – Controlos
TextField textFieldName = new TextField();
▸ TextField – Criação e Utilização textFieldName.setMinSize(12, 10);

1. Criar um objeto do tipo TextField.


2. Para ler o conteúdo introduzido num String name= textFieldName.getText();
TextField usa-se o método getText();

TextField

7
JavaFX – Controlos
▸ Label – Criação e Utilização Label nameLabel = new Label("Nome");
Label listLabel = new Label("ListaPessoas");

1. Criar um objeto do tipo Label.

Label

8
JavaFX – Controlos
ListView
▸ ListView – Criação e Utilização

1. Criar um objeto do tipo ListView. ListView<String> listOfNames= new ListView<>();

2. Criar uma ObservableList de


Strings e associá-la à ListView
ObservableList<String> items=FXCollections.observableArrayList();
listOfNames.setItems(items);

3. Adicionam-se linhas de texto à


items.add(nome);
ObservableList para preencher a ListView

9
JavaFX – Classe FXCollections
▸ A classe FXCollections disponibiliza um conjunto de métodos de classe (static) que
permitem obter e manipular "coleções sincronizáveis":
▹ observableArrayList()
▹ observableSet()
▹ observableHashMap()
▹ replaceAll
▹ reverse
▹ rotate
▹ sort
▹ etc.
▸ As "coleções sincronizáveis" ao serem modificadas (adicionar ou remover elementos) tentam
atualizar os objetos com que estão sincronizadas (normalmente elementos gráficos). Na
próxima aula será explicado este mecanismo de sincronização.

10
Exemplo
Controlos simples
▸ JavaFX – Controlos

11
mplo Completo (ListView, TextField)

public void start(Stage primaryStage) {


Objetivo: primaryStage.setTitle("Exemplo Controlos");
VBox root = new VBox();
addTextPanel (root);
addListPanel (root);
Preencher a lista com os nomes primaryStage.setScene(new Scene(root, 400, 250));
introduzidos no TextField. primaryStage.show();
}

PanelTexto
O nome introduzido é transferido
para a lista após validado pelo
utilizador através do botão OK! PanelList

12
JavaFX – Exemplo: adicionarPanelTexto
private TextField textFieldName; Atributo na Classe
public void addTextPanel (Pane root) {

HBox textPanel = new HBox();


Panel (HBox)
textPanel.setPadding(new Insets(10));
textPanel.setSpacing(10);

Label nameLabel = new Label("Nome"); Label

this.textFieldName = new TextField(); TextField


this.textFieldName.setMinSize(12, 10);

Button okButton = new Button("OK"); Button


okButton.setOnAction(e -> addListName());

textPanel.getChildren().addAll(nameLabel,this.textFieldName,okButton);
root.getChildren().add(textPanel);
}

13
JavaFX – Exemplo: adicionarPanelList

private ObservableList<String> items;


private ListView<String> listOfNames; Atributos na Classe

public void addListPanel (Pane root) {


VBox listPanel = new VBox();
listPanel.setPadding(new Insets(10)); Panel (VBox)
listPanel.setSpacing(10);

Label listLabel = new Label("Lista Pessoas"); Label

this.items= FXCollections.observableArrayList();
this.listOfNames = new ListView<>();
ListView
this.listOfNames.setPrefSize(100,120);
this.listIfNames.setItems(this.items);

listPanel.getChildren().addAll(namesLabel,this.listOfNames);
root.getChildren().add(listPanel);
}

14
JavaFX – Exemplo: adicionarNomeLista

Método que é executado quando o utilizador public void addNameToList() {


clica no botão OK! String name = this.textFieldName.getText();
if (!name.isEmpty()) {
▸ O texto introduzido no TextField this.items.add(name);
(textFieldNome). }

▸ é adicionado aos items associados à this.textFieldName.setText("");


ListView }

▸ O campo do TextField é colocado


a vazio

15
JavaFX – Exemplo: Apagar elementos da lista

Objetivo:
Selecionar um nome da lista e apagá-lo.

▸ Adicionar um botão para apagar


▸ Associar a acção de remover um
elemento da lista
▸ Remover o elemento selecionado

16
JavaFX – Exemplo: Apagar elementos da lista
public void addListPanel(Pane painel) {

...

Button deleteButton = new Button("Delete");


Objetivo: deleteButton.setOnAction(e -> removeNameFromList());

Selecionar um nome da lista e apagá-lo. ...


listPanel.getChildren().addAll(namesLabel, this.listOfNames,
▸ Adicionar um botão para apagar deleteButton);
...
▸ Associar a acção de remover um }
elemento da lista
▸ Remover o elemento selecionado public void removeNameFromList() {
da lista items. int index;
index=this.listOfNames.getSelectionModel().getSelectedIndex();
if (index != -1) {
this.items.remove(index);
}
}

17
JavaFX – Exemplo: Aceder aos elementos selecionados

Singular
Numa Lista podemos ter: o String item =
1. Selecção Singular list.getSelectionModel().getSelectedItem();

podemos aceder ao:


o int index =
1. Item selecionado
list.getSelectionModel().getSelectedIndex();
2. Índice do item selecionado

2. Selecção Múltipla
Múltipla
podemos aceder aos: o ObservableList<String> items=
1. Itens selecionados list.getSelectionModel().getSelectedItems();
2. Índices dos itens selecionados
o ObservableList<Integer> indexes =
list.getSelectionModel().getSelectedIndices();

18
JavaFX – Exemplo Controlos Simples

19
Desenhador de
Formas
▸ JavaFX – Controlos

20
JavaFX Exemplo – Desenhador de formas

▸ Criar uma aplicação que


permita o desenho de diversas
formas geométricas, com
diversas cores:

21
JavaFX Exemplo – Desenhador de formas
▸ Serão apresentadas duas ListView que permitem indicar a forma e a cor pretendidas. Estas
serão colocadas num painel VBox;
▸ Através do uso do rato indica-se (carregando) o ponto inicial e (levantando) o ponto final;
▸ Em oposição à criação de objetos das subclasses de Shape, apresentados na aula de
introdução, será criada uma zona de desenho (Canvas), e utilizam-se os métodos de desenho
de GraphicsContext para criar todas as formas;
▸ GraphicsContext (http://docs.oracle.com/javase/8/javafx/api/javafx/scene/canvas/GraphicsContext.html) é uma classe que contém toda
a informação necessária ao desenho (cores, espessuras, etc.) e os métodos necessários:
setFill, getFill, setStroke, getStroke, fillArc, strokeArc,
beginPath, closePath, moveTo, lineTo, fill, stroke, fillOval,
strokeOval, fillRect, strokeRect, fillRoundRect, strokeRoundRect,
strokeLine, fillText, strokeText, etc.

22
JavaFX Exemplo – Alternativas de formas
public enum Shape {
Oval, Retangulo, RetArredondado;
//Foi quebrada a norma de escrita em MAIÚSCULAS para não
//rescrever o toString para ficar "bonitinho"

private static final double ROUNDING = 5; //RetArredondado

▸Criar um Enum com as public void drawShape (GraphicsContext graphicsContext,


double x, double y,
alternativas de formas: Ovais, double largura, double height) {

Retângulos e Retângulos
switch (this) {
case Oval:
Arredondados graphicsContext.fillOval(x, y, width, height);
break;
case Retangulo:
graphicsContext.fillRect(x, y, width, height);
break;
▸É criado o método que desenha a case RetArredondado:
graphicsContext.fillRoundRect(x,y, width, height,
forma, em função do valor atual width /Shape. ROUNDING,altura/ Shape. ROUNDING);
break;
}
}
}
23
JavaFX Exemplo – Seletor de Cores e Formas
public class ShapeColorSelector extends VBox {

private static final double SPACING = 10.0;


private static final double LARGURA_LISTA = 130.0;
private static final double ALTURA_LISTA = 150.0;
▸O seletor de formas e cores será um private static final String[] NAMES_colors =
{"Aqua", "Black", "Blue", "Fuchsia", "Gray",
VBox com duas ListView "Green", "Lime", "Maroon", "Navy", "Olive",
"Orange", "Purple", "Red", "Silver", "Teal",
(atributos da classe) e dois Label "White", "Yellow"};

▸São definidas constantes com as private ListView<Shape> shapes;


private ListView<String> colors;
dimensões dos controlos
▸A constante NAMES_colors
public ShapeColorSelector() {
Label shapeLabel = new Label("Escolha uma Forma:");
contém as String com os nomes de Label colorsLabel = new Label("Escolha uma Cor:");
shapes = criarListView(Shapes.values());
algumas cores standard (as originais colors =
criarListView(ShapeColorSelector.NAMES_Colors);
em HTML) getChildren().addAll(
shapeLabel,shapes, colorsLabel, colors);
setSpacing(SeletorFormaCor SPACING);
}
24
JavaFX Exemplo – Seletor de Cores e Formas
private <T> ListView<T> criarListView(T[] valores)
{
ListView<T> result = new ListView<>();
result.setItems(FXCollections.observableArrayList(val
ores));
▸Criar um método genérico (tipo de result.getSelectionModel().select(0);
result.setPrefWidth(SeletorFormaCor.LARGURA_LISTA);
elementos), que recebe um array com result.setPrefHeight(SeletorFormaCor.ALTURA_LISTA);
return result;
os elementos (de diferentes tipos) a }

serem colocados na ListView public Forma formaEscolhida() {


return formas.getSelectionModel().getSelectedItem();
}
▸Criar os dois métodos inspetores que public Color corEscolhida() {
devolvem as escolhas da forma e cor String nomeCor = cores.getSelectionModel().getSelected
Item();
return Color.valueOf(nomeCor);
}

25
JavaFX Exemplo – Área de desenho
public class DrawingArea extends Canvas {

private static final double WIDTH = 600;


Criar um Canvas que tem definido os private static final double EIGHT = 400;

seus atributos (shapeColorSelector, private SeletorFormaCor shapeColorSelector;


private double xInicial;
recebido como argumento no Construtor, e private double yInicial;

as coordenadas iniciais). public DrawingArea(SeletorFormaCor shapeColorSelector) {


super(DrawingArea.WIDTH, DrawingArea.EIGHT);
Criar, ainda, os seus EventHandler de rato this.shapeColorSelector = shapeColorSelector;
setOnMousePressed(this:: fixStart);
carregado (início da figura: setOnMouse }
setOnMouseReleased(this:: drawShape);

Pressed) ou levanta- do (fim da figura:


private void fixStart(MouseEvent event) {
setOnMouseReleased) xInicial = event.getX();
yInicial = event.getY();
}

o O método associado ao setOnMousePressed (fixarInicio)


regista, nos atributos, as coordenadas do ponto onde começou a
forma a desenhar-se 26
Expressões lamba versus referenciação de métodos
▸ Quando uma expressão lambda se limita a executar um método:
e -> fixStart(e) ou e -> drawShape(e)
▸ E esse método tem uma assinatura semelhante à do EventHandler:
void handle(MouseEvent event)
▸ Então pode-se indicar apenas o nome do método que deverá ser executado (antecedendo-o com a indicação do
objeto onde o método será executado):
this::fixStart ou this::drawShape
▸ Se o método fosse static então seria indicado o nome da classe antes do método:
NomeDaClasse::NomeDoMétodo
http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

27
JavaFX Exemplo – Área de desenho
▸ O método associado ao setOnMouseReleased é responsável por armazenar as
coordenadas do ponto onde termina a forma a desenhar-se, por calcular os limites da forma,
por preparar o ambiente de desenho e, finalmente, por mandar desenhar a forma:
private void drawShape(MouseEvent event) {
double xFinal = event.getX();
double yFinal = event.getY();

double x = Math.min(xInicial, xFinal);


double y = Math.min(yInicial, yFinal);
double width = Math.abs(xFinal - xInicial);
double eight = Math.abs(yFinal - yInicial);

GraphicsContext graphicsContext = getGraphicsContext2D();


Color chosenColor = shapeColorSelector.chosenColor();
graphicsContext.setFill(chosenColor);
graphicsContext.setStroke(chosenColor);
shapeColorSelector.chosenShape().drawShape(graphicsContext, x, y, width, eight);
}
} 28
JavaFX Exemplo – desenharForma
▸O método drawShape, associado ao setOnMouseReleased , começa por registar as
coordenadas do ponto onde termina a forma a desenhar-se;
▸Calcula, em seguida, as coordenadas do canto superior esquerdo (menores coordenadas x e y) e a
largura e altura para a respetiva forma (diferença entre as coordenadas);
▸Fixa a cor a utilizar em função da cor escolhida (chosencolor);
▸Executa o método drawShape da forma escolhida, obtida com o método chosencolor, passando
o GraphicsContext (obtido por getGraphicsContext2D) da Canvas e os valores
calculados previamente.

29
JavaFX Exemplo – método start

▸A aplicação principal é apenas


public class CanvasDemo extends Application {

constituída por um HBox que conterá @Override


public void start(Stage primaryStage) {
um ShapeColorSelector e um HBox root = new HBox();
ShapeColorSelector shapeColorSelector = new ShapeColorSelector();
DrawingArea (que recebe o root.getChildren().addAll(shapeColorSelector,
new DrawingArea(shapeColorSelector));
ShapeColorSelector como primaryStage.setTitle("Formas");
primaryStage.setScene(new Scene(root));
argumento): }
primaryStage.show();

public static void main(String[] args) {


launch(args);
}
}

30
Resumindo
▸ Controlos
▹ Button – inserir imagem num botão
1. Criar uma imagem associada a um ficheiro (Image).
2. Associar a imagem ao botão (ImageView).

▹ TextField – criação e utilização


1. Criar um objeto do tipo TextField.
2. Usar getText() para ler conteúdo e o setText() para o alterar.

▹ Label – criação e utilização


1. Criar um objeto do tipo Label.

▹ ListView – criação, utilização e preenchimento


1. Criar um objeto do tipo ListView.
2. Criar uma ObservableList e associá-la à ListView (será utilizado o toString)
3. Adicionar ou remover linhas à ObservableList para preencher a ListView

▹ Uso de Canvas e GraphicsContext 31


Leitura Complementar
▸ Chapter 2 – JavaFX Fundamentals Pgs 31 a 51
▸ Chapter 4 – Layouts and UI Controls Pgs 108 a 111
▸ Chapter 5 – Graphics with JavaFX Pgs 123 a 139
▸ Documentação:
▹ http://docs.oracle.com/javase/8/javafx/api/toc.htm
▹ Controlos:
▹ http://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/package
-frame.html
▹ http://docs.oracle.com/javase/8/javafx/user-interface-
tutorial/ui_controls.htm#JFXUI336
▹ Desenhar no Canvas:
▹ http://docs.oracle.com/javase/8/javafx/graphics-
tutorial/canvas.htm#JFXGR214 32
InputEvent

33
Programação Orientada por Objetos

JavaFX –
Propriedades

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Visualizador de Pessoas
▹ Criação de uma interface gráfica
▹ Especializar (herança) objetos gráficos para
representar a nossa informação
▸ Propriedades e Binding

2
EXEMPLO VISUALIZADOR
PESSOAS
▸ JavaFX – Propriedades
Exemplo – Visualizador de Pessoas
▸ Requisitos da aplicação:
▹ Criar uma aplicação que permita gerir a informação de
um grupo de pessoas.
▹ A informação a guardar para cada pessoa é apenas o
nome e a idade.
▹ Deve ser possível adicionar, alterar, visualizar e apagar
uma pessoa.
▹ A visualização deve ser feita em JavaFX em duas cenas
diferentes, uma que mostra as pessoas e outra que
visualiza apenas uma pessoa.

4
Criação de uma interface gráfica
public class Person {
▸ Supor que temos uma classe Person, que private static final int MINIMUM_AGE = 0;
representa a informação associada a uma private static final int MAXIMUM_AGE = 120;
private String name; private int age;
pessoa:
▹ Nome protected static boolean isNameValid(String name) {
return (name != null && !name.isEmpty());
▹ Idade }
protected static boolean isAgeValid(int age) {
▸ Definir a classe com a garantia de não haver return ((age >= MINIMUM_AGE) && (age <= MAXIMUM_AGE)); }
public Person(String name, int age) {
informação inconsistente: if (!isNameValid(name)) {
throw new IllegalArgumentException("O nome da pessoa não
▹ Existe o nome é válido!");
}
▹ A idade é [0,120] if (!isAgeValid(age)) {

throw new IllegalArgumentException("A idade da pessoa
Em caso de valores impossíveis o construtor não é válida!");
lança exceções }
this.name = name;
this.age = age;
}
5
public String getName() {
Classe Person (cont.) }
return name;

public void setName(String name) {


if (isNameValid(name)) {
this.name = name;
▸ Tradicionais getters (inspetores) }
}
public int getAge() {
▸ Nos setters (modificadores) é return age;
}
public void setAge(int age) {
validado o valor antes da alteração if (isAgeValid(age)) {
this.age = age;
}
▸ Criar getters para as constantes, }
public static int getMinimumAge() {
return MINIMUM_AGE; }
para serem acessíveis externamente public static int getMaximumAge() {
return MAXIMUM_AGE; }
@Override
▸ Convém redefinir o toString public String toString() {
return getName() + " (" + getAge() + " anos)";
}
}
6
Criar uma coleção de Pessoas
public class Roster {
▸ Criar uma classe para armazenar diversas
private HashSet<Person> persons;
pessoas


public Roster() {
Optar por um conjunto pois não devem existir persons = new HashSet<>();
}
pessoas repetidas
public void addPerson(Person person) {
▸ Implementar os modificadores (adicionar e }
persons.add(person);

remover) e os inspetores (verificar se está public void removePerson(Person person) {


persons.remove(person);
contido e obter, num array, todos os elementos) }
public boolean containPerson(Person person) {
return persons.contains(person);
}
public Person[] getPersons() {
return persons.toArray(new Person[persons.size()]);
}
}
7
Criar uma coleção de Pessoas - Alternativa
▸ Caso não seja necessária nenhuma ação
adicional nos métodos que acedem à coleção public class Roster extends HashSet<Person> {
}
(podem sempre ser Override).
@Override
▸ E caso não seja contraindicado utilizar os nomes public boolean add(Person person) {
System.out.println("Adicionei: " + person);
originais dos métodos da coleção. return super.add(person); }

▸ É possível definir a classe Roster como uma @Override public boolean remove(Object person)
{
extensão de HashSet<Person>. System.out.println("Removi: " + person);
return super.remove(person);
▸ Os métodos addPerson, removePerson e }
}
containPerson, passam a ser os add, remove

e contains herdados da superclasse


HashSet<Person>.
8
Definir a interface gráfica
▸ Será necessário ter dois ecrãs:
▹ Visualização e alteração da coleção
▹ Visualização, criação e alteração dos dados de
uma pessoa
▸ O utilizador controla a troca de écrans através dos
botões Criar/Editar e Aceitar/Cancelar
▸ Serão criadas subclasses de VBox e de GridPane
para devolverem os painéis prontos, com todos os
elementos gráficos necessários
▸ Os construtores recebem os objetos com a
informação a apresentar nos elementos gráficos
9
Visualizador da coleção Roster
▸ É criada uma subclasse de VBox para apresentar a coleção de pessoas. O painel
apresentará:
▹ O rótulo
▹ A lista
▹ Os botões

public class RosterViewer extends VBox {


public RosterViewer(final Roster persons) {
//Criar o rótulo
Label listLabel = new Label("Pessoas");

//Criar a lista
final ObservableList<Person> personsList = FXCollections.observableArrayList(persons);
final ListView<Person> list = new ListView<>(personsList);
10
Visualizador da coleção Roster (cont.)
//Criar o painel dos botões
HBox buttonsPanel = new HBox(10);
//Botão criar
Button createButton = new Button("Criar...");
createButton.setOnAction(e -> list.getScene().setRoot(new PersonViewer(persons, null)));

//Botão editar
Button editButton = new Button("Editar...");
editButton.setOnAction(e -> {
Person person = list.getSelectionModel().getSelectedItem();
if (person != null) {
list.getScene().setRoot(new PersonViewer(persons, person)); } });

//Botão apagar
Button removeButton = new Button("Apagar");
removeButton.setOnAction(e -> {
Person person = list.getSelectionModel().getSelectedItem();
if (person != null) {
personsList.remove(person);
persons.remove(person);
}
});

buttonsPanel.getChildren().addAll(createButton, editButton, removeButton);


Visualizador da coleção Roster (cont.)
▸ Depois de criados todos os elementos, estes devem ser inseridos no respetivo painel com espaçamentos
definidos (Padding e Spacing).
//Posicionar os nós

setPadding(new Insets(10));
setSpacing(10);
getChildren().setAll(listLabel, list, buttonsPanel);
}
}
▸ Nesta classe só se definiu:
▹ O construtor para inserir os elementos gráficos necessários
▹ Os EventHandler dos botões que manipulam a coleção (argumento do construtor – final)
Todas as restantes funcionalidades são herdadas do painel VBox
▸ A alternância entre ecrãs é feita através de xxx.getScene().setRoot(), onde xxx representa um
elemento gráfico colocado no painel. Assim ter-se-á acesso à scene onde o painel está a ser apresentado.
12
Visualizador de Pessoa
▸ É criada uma subclasse de GridPane para apresentar uma Person.
O painel apresentará:
▹ Os rótulos
▹ A caixa de texto para o nome
▹ O slider para a idade
▹ Os botões
public class PersonViewer extends GridPane {

public PersonViewer(final Roster persons, final Person person){


//Criar os nós
Label nameLabel = new Label("Nome");
final TextField nameField = new TextField();
nameField.setPrefWidth(200);
Label ageLabel = new Label("Idade");
final Slider ageSlider = createAgelider();
if (person != null) {
nameField.setText(person.getName());
ageSlider.setValue(person.getAge());
}

13
Visualizador de Pessoa – criação do Slider
▸ A criação do Slider deve utilizar os limites da idade definidos na classe Pessoa,
acrescentamos, à classe atual (PersonViewer), um método que cria e devolve um
Slider, adaptado à informação das idades das pessoas:
public class PersonViewer extends GridPane {

public final Slider createAgeSlider() {


Slider slider = new Slider();
slider.setMin(Person.getIdadeMinima());
slider.setMax(Person.getIdadeMaxima());
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);
slider.setMajorTickUnit(10);
return slider;
}
}
14
Criação do Slider - nova classe
▸ Alternativamente a criação do Slider poderia ser feita através da especialização da
classe Slider, criando-se um tipo de Slider específico para as idades. Basta
copiar o conteúdo do método anterior para o construtor dessa nova classe
(AgeSlider):

public class AgeSlider extends Slider {

public AgeSlider() {
setMin(Person.getMinimumAge());
setMax(Person.getMaximumAge());
setShowTickLabels(true);
setShowTickMarks(true);
setMajorTickUnit(10);
}
}
15
Criação do Slider - nova classe (cont.)
▸ Neste segundo caso, será necessário substituir a criação do Slider pela criação de um objeto da
nova classe (AgeSlider) na classe PersonViewer,
public class PersonViewer extends GridPane {

public PersonViewer(final Roster persons, final Person person) {



final Slider ageSlider = createAgeSlider();
final AgeSlider ageSlider = new AgeSlider();

}
}

▸ O método anterior, criarSliderIdade(), da classe VisualizadorPessoa deve ser eliminado.

16
Visualizador de Pessoa (cont.)
▸ Depois de criados os elementos para apresentar a informação da pessoa convém acrescentar à janela
PersonViewer os botões aceitar e cancelar:
//Botão aceitar
Button acceptButton = new Button("Aceitar");
acceptButton.setOnAction(e -> {
if (person == null) { //Criar
try {
persons.addPerson(new Person(nameField.getText(), (int) ageSlider.getValue()));
} catch (Exception ex) {
}
} else { //Atualizar
person.setName(nameField.getText());
person.setAge((int) ageSlider.getValue());
}
nameField.getScene().setRoot(new RosterViewer(persons));
});

Button cancelButton = new Button("Cancelar");


cancelButton.setOnAction(e ->
nameField.getScene().setRoot(new RosterViewer(persons)));
17
Visualizador de Pessoa (cont.)
▸ No final da criação dos botões são colocados todos os elementos gráficos:

//Posicionar os nós
setPadding(new Insets(10));
setVgap(10);
setHgap(10);
add(nameLabel, 0, 0);
add(nameField, 1, 0);
add(ageLabel, 0, 1);
add(ageSlider, 1, 1);
add(acceptButton, 0, 2);
add(cancelButton, 1, 2);
}
}

18
Gravar a informação
▸ Com esta interface é possível ir acrescentando, modificando e apagando a informação associada às pessoas.
▸ A manutenção da informação entre execuções do programa passa por gravar, num ficheiro, toda a informação
▸ É preciso tornar Serializable as classes (Roster e Person) e definir métodos, ReadRoster() e writeRoster(), para ler (no início
do programa) e escrever (associado ao botão fechar ) a informação de/para o ficheiro:

public class PersonEditor extends Application {

@Override
public void start(Stage primaryStage) {
final Roster persons = Roster.readRoster();
Scene scene = new Scene(new RosterViewer(persons), 400, 300);
primaryStage.setTitle("Gestão de Pessoas");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest(e -> persons.writeRoster());
primaryStage.show();
}
19
Gravar a informação
public class Roster extends HashSet<Person> implements Serializable {

private static final String FILE_NAME = "roster.dat";


...
public void writeRoster() {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
oos.writeObject(this);
oos.flush();
oos.close();
} catch (IOException e) {
System.out.println(e.getMessage()); } }

public static Roster readRoster() {


Roster persons;
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME));
persons = (Roster) ois.readObject();
ois.close();
} catch (IOException | ClassNotFoundException e) {
System.out.println(e.getMessage());
persons = new Roster();
}
return persons;
}
} 20
Propriedades

▸ JavaFX – Propriedades
Exemplo – Visualizador de Pessoas
▸ Requisitos da aplicação:
▹ Criar uma aplicação que permita gerir a informação de
um grupo de pessoas.
▹ A informação a guardar para cada pessoa é apenas o
nome e a idade.
▹ Deve ser possível adicionar, alterar, visualizar e apagar
uma pessoa.
▹ A visualização deve ser feita em JavaFX em duas cenas
diferentes, uma que mostra as pessoas e outra que
visualiza apenas uma pessoa.

22
Exemplo – Visualizador de Pessoas

23
Sincronização da informação
▸ A escolha da introdução da informação relativa à idade através de um Slider é
visualmente apelativa mas não permite a introdução de informação de forma precisa,
▸ O ideal era ter a possibilidade de introdução da informação através do Slider e de uma
Caixa de Texto, de forma sincronizada:

A informação introduzida no Slider e na Caixa de


Texto está sincronizada: mudando um, muda o outro

24
Sincronização da informação
▸ O Slider tem o getter e o setter tradicionais: getValue() e setValue(). No entanto o valor é "armazenado" dentro de um
objeto especial que se obtém através de valueProperty() e não num simples double.

▸ Analogamente, a Caixa de Texto tem o getter e o setter tradicionais: getText() e setText(), mas o seu valor é
"armazenado" dentro de um objeto especial que se obtém através de textProperty() e não numa simples String.

▸ Estes objetos especiais, as "propriedades", permitem associações entre eles, por forma a sincronizar a sua
informação. Esta sincronização pode ser bidirecional (a alteração de um implica, automaticamente, a alteração do
outro) ou unidirecional (só a mudança de um é que influência a mudança do outro).

▸ A sincronização bidirecional é feita através do método bindBidirectional e a unidirecional através de bind.


▸ É possível desfazer a sincronização automática através dos métodos unbindBidirectional e unbind, respetivamente.
25
Propriedades
▸ Existem propriedades para armazenar quase todos os tipos de valores primitivos:
▹ BooleanProperty, DoubleProperty, FloatProperty, IntegerProperty, LongProperty
▹ Existem ainda as propriedades ObjectProperty e StringProperty, para armazenar objetos
genéricos e Strings
▸ As anteriores classes são abstratas e devem ser especializadas para implementar funcionalidades específicas. No
entanto estão disponíveis para instanciação as seguintes classes concretas:
▹ SimpleBooleanProperty, SimpleDoubleProperty, SimpleFloatProperty,
SimpleIntegerProperty, SimpleLongProperty, SimpleObjectProperty,
SimpleStringProperty
▸ Podemos modificar os tipos dos atributos das nossas classes, alterando os seus tipos primitivos, para as
propriedades equivalentes:
▹ ex.: de private int age para private IntegerProperty age e obter atributos sincronizáveis
(usar get() e set()).

26
Propriedades – Listener
▸ As propriedades permitem, para além da sincronização, associar um Listener, que será
executado sempre que o valor da propriedade é alterado.
▸ Desta forma podemos implementar comportamentos genéricos, que excedem a simples
sincronização do valor. Por exemplo, para uma propriedade DoubleProperty (exemplo com o
Slider da idade):

ageSlider.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue,
Number newValue) {
"Código a executar quando a propriedade muda de valor. Temos acesso:
à propriedade (observable),
ao valor original (oldValue)
ao novo valor (newValue)"
}
});
27
Propriedades no JavaFX
▸ As propriedades são um mecanismo fundamental do JavaFX.
▸ Todos os objetos gráficos têm propriedades para os seus diversos valores. O teste
hasProperties() verifica se um Node tem ou não propriedades.
▸ As propriedades são acessíveis através de métodos da forma xxxProperty().

▸ As propriedades são manipuladas através de get() e set().


▸ A sincronização de propriedades vai ter um papel preponderante na implementação de
animações no JavaFX, pois a sincronização de valores permitirá que estes (posição,
dimensão, outras características, etc.) mudem ao longo do tempo da animação.

28
Slider e Caixa de Texto
▸ Para resolver o problema de implementar um Slider sincronizado com uma Caixa de Texto, vai ser
necessário criar uma classe que substitua a classe Slider, pelo menos nas suas funcionalidades
básicas utilizadas no presente projeto (aceder através de getValue() e setValue()).
▸ Assim, na classe PersonViewer, substitui-se a criação do AgeSlider pela criação de um objeto da
nova classe (AgeViewer):

public class PersonViewer extends GridPane {


public PersonViewer(final Roster persons, final Person person) {

final AgeSlider ageSlider = new AgeSlider();
final AgeViewer ageSlider = new AgeViewer();

}
}

29
Classe AgeViewer
public class AgeViewer extends HBox {
▸ A implementação da
classe AgeViewer private final AgeSlider ageSlider;
private final TextField ageField;
apenas necessita dos public AgeViewer() {
atributos para conter os ageSlider = new AgeSlider();
ageField = new TextField();
elementos gráficos, do ageField.setPrefWidth(50);
ageField.textProperty().bindBidirectional(ageSlider.valueProperty(),
construtor e do getter e NumberFormat.getIntegerInstance());
setter: setSpacing(10);
getChildren().addAll(ageSlider, ageField);
}
public double getValue() {
return ageSlider.getValue();
}
public void setValue(double age) {
ageSlider.setValue(age);
}
}

30
Resumindo
▸ Criação de interfaces gráficas através do acrescento de novas classes que permitem a visualização dos
objetos já existentes no domínio do problema.
▸ Para cada classe poderá ser criado um "visualizador” (implementa as operações básicas – CRUD: Create, Read,
Update e Delete).
▸ A criação dos visualizadores é feita por herança de objetos gráficos já existentes. Uma vez que se pretende
apresentar informação dos vários atributos, recorre-se, normalmente, à extensão das classes que permitem
agrupar/apresentar diversos elementos gráficos – os painéis. No respetivo construtor (que recebe o objeto a
apresentar) são criados todos os elementos gráficos necessários.
▸ A funcionalidade fica completa através da criação dos diversos EventHandler e da ligação (binding) entre
as propriedades do objeto e a dos elementos gráficos. Esta ligação pode ser feita de forma bidirecional
(bindBidirectional) ou unidirecional (bind), para sincronizar os valores ou genericamente através da
criação de um Listener.
31
Leitura Complementar
▸ Chapter 3 – Lambdas and Properties Pgs 74 a 89
Chapter 4 – Layouts and UI Controls Pgs 91 a 101 e 108 a 111

▸ Propriedades e binding:
▸ http://docs.oracle.com/javase/8/javafx/properties- binding-
tutorial/binding.htm#sthref8

32
Programação Orientada por Objetos

JavaFX – Controlos II

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2022/2023
Sumário

▸ Controlos ListView e ComboBox

▸ Controlos Tabs, Accordion e TitledPane

▸ Menus em JavaFX

▸ Controlos Checkbox e Radiobutton

2
LISTVIEW E COMBOBOX

▸ JavaFX – Controlos II
JavaFX - Controlos
▸ As classes para criar controlos
encontram-se no pacote
▹ javafx.scene.control
▸ Exemplos anteriores:
▹ Button
▹ TextField
▹ Label
▹ ListView

4
JavaFX – Revendo o exemplo da ListView
o ListView –
Criação e Utilização
ListView
1. Criar um objeto do tipo
ListView.

2. Criar uma
ObservableList de
Strings e associá-la ListView<String> listOfNames = new ListView<>();
à ListView
ObservableList<String> items=FXCollections.observableArrayList();
listOfNames.setItems(items);

3. Adicionar linhas de texto


à ObservableList
para preencher a
items.add(name);
ListView
5
JavaFX – Usando uma ComboBox
o ComboBox –
Criação e Utilização
ComboBox
1. Criar um objeto do tipo
ComboBox.

2. Criar uma
ObservableList de
Strings e associá-la ComboBox<String> listOfNames= new ComboBox<>();
à ComboBox
ObservableList<String> items=FXCollections.observableArrayList();
listOfNames.setItems(items);

3. Adicionar linhas de texto


à ObservableList
para preencher a
items.add(name);
ComboBox
6
JavaFX- Eventos : Exemplo

7
Tabs, Accordion
e TitledPane

▸ JavaFX – Controlos II

8
JavaFX – Exemplo: Tab
Uma forma simples de gerir a alternância entre vários painéis é através da utilização de um
TabPane com vários Tab associados.

TabPane
Tab

9
JavaFX – Exemplo: Tab
Objetivo: Ter dois Painéis A e B, e alternar entre um e outro consoante o Tab selecionado.

public class PanelWithTab extends StackPane {

public PanelWithTabs () {
TabPane tabPane = new TabPane();
Tab tabA= new Tab("Painel A");
Tab tabB= new Tab("Painel B");
tabA.setContent(new PanelA());
tabB.setContent(new PanelB());
tabPane.getTabs().addAll(tabA,tabB);
this.getChildren().add(tabPane);
}
}

public class PanelA extends StackPane {

public PanelA() {
this.getChildren().add(new Label("Painel A"));
}
}

10
JavaFX – Exemplo: Tab
Objetivo: Ter dois Painéis A e B, e alternar entre um e outro consoante o Tab selecionado.

public class PanelWithTab extends StackPane {

public PainelWithTab () { 1. Criar a TabPane


TabPane tabPane = new TabPane();
Tab tabA= new Tab("Painel A"); 2. Criar objetos do tipo Tab.
Tab tabB= new Tab("Painel B");
tabA.setContent(new PanelA()); 3. Associar a cada Tab o Node pretendido.
tabB.setContent(new PanelB()); Nota: PanelA e PanelB são classes
tabPane.getTabs().addAll(tabA,tabB); derivadas de Node, normalmente painéis
this.getChildren().add(tabPane); (GridPane, HBox, VBox, …).
}
} 4. Associar cada Tab ao TabPane

Para seleccionar um tab:


public class PanelA extends StackPane { tabPane.getSelectionModel()
.select(1);
public PanelA() {
this.getChildren().add(new Label("Painel A"));
}
}

11
JavaFX – Exemplo: Accordion

Outra forma simples de gerir a alternância entre vários painéis é através da utilização de um Accordion com
vários TitledPane associados.

TitledPane
Accordion

12
JavaFX – Exemplo: Accordion
Objetivo: Ter dois Painéis 1 e 2, e alternar entre um e outro consoante a seleção.

public class PanelWithAccordion extends StackPane {

public PanelWithAccordion() {
Accordion accordion = new Accordion();
TitledPane panel1 = new Panel1();
TitledPane panel2 = new Panel2();
accordion.getPanes().add(panel1);
accordion.getPanes().add(panel2);
this.getChildren().add(accordion);
}
}

public class Panel1 extends TitledPane {

public Panel1() {
this.setText("Painel 1");
this.setContent(new Label("Painel 1"));
}
}

13
JavaFX – Exemplo: Accordion

Objetivo: Ter dois Painéis 1 e 2, e alternar entre um e outro consoante a seleção.

public class PanelWithAccordion extends StackPane {


1. Criar o Accordion
public PanelWithAccordion() {
Accordion accordion = new Accordion();
2. Criar objetos do tipo
TitledPane panel1 = new Panel1();
TitledPane.
TitledPane panel2 = new Panel2();
Nota: É preciso definir o título
accordion.getPanes().add(panel1);
(setText) e o conteúdo
accordion.getPanes().add(panel2);
(setContent) dos
this.getChildren().add(accordion);
TitledPane.
}
}
3. Associar cada TitledPane ao
Accordion.
public class Panel1 extends TitledPane { Para selecionar um TitledPane :
accordion.setExpandedPane
public Panel1() { (painel2);
this.setText("Painel 1");
this.setContent(new Label("Painel 1"));
}
}
14
JavaFX- Eventos : Exemplo

15
Menus em
JavaFX
▸ JavaFX – Controlos II

16
JavaFX – Exemplo: Menus
Em JavaFX podem usar-se as seguintes classes para definir menus:
• MenuBar
• Menu
• MenuItem
• CheckMenuItem
• RadioMenuItem
• SeparatorMenuItem
• ContextMenu

MenuItem MenuBar
Menu

SeparatorMenuItem

CheckMenuItem

17
JavaFX – Exemplo: Menus
Preparação do Layout
public class PanelWithMenu extends BorderPane {
public PanelWithMenu() {
MenuBar menuBar = new MenuBar();
Menu setupMenu = new Menu("Configurar");
MenuItem setupMenuA = new MenuItem("Adicionar A");
MenuItem setupMenuB = new MenuItem("Adicionar B");
setupMenu.getItems().addAll(setupMenuA, setupMenuB);
Menu showMenu = new Menu("Visualizar");
MenuItem showMenuA = new MenuItem("ver A");
CheckMenuItem showMenuB = new CheckMenuItem("ver B");
showMenu.getItems().addAll(showMenuA,new SeparatorMenuItem(), showMenuB);
Menu exitMenu = new Menu("Sair");
MenuItem closeMenu = new MenuItem("Fechar");
exitMenu.getItems().add(closeMenu);
menuBar.getMenus().addAll(setuMenu,
showMenu,
exitMenu);
this.setTop(menuBar);
}
}
18
JavaFX – Exemplo: Menus Preparação do funcionamento
Ex: Sair da aplicação através do MenuItem Fechar do Menu Sair

public class PanelWithMenu extends BorderPane {


public PanelWithMenu() {
MenuBar menuBar = new MenuBar();
Menu setupMenu = new Menu("Configurar");
MenuItem setupMenuA = new MenuItem("Adicionar A");
MenuItem setupMenuB = new MenuItem("Adicionar B");
setupMenu.getItems().addAll(setupMenuA, setupMenuB);
Menu showMenu = new Menu("Visualizar");
MenuItem showMenuA = new MenuItem("ver A");
CheckMenuItem showMenuB = new CheckMenuItem("ver B");
showMenu.getItems().addAll(showMenuA,new SeparatorMenuItem(), showMenuB);
Menu exitMenu = new Menu("Sair");
MenuItem closeMenu = new MenuItem("Fechar");
exitMenu.getItems().add(closeMenu);
closeMenu.setOnAction(
e -> Platform.exit()
);

menuBar.getMenus().addAll(setuMenu,
showMenu,
exitMenu);
this.setTop(menuBar);
}
}

19
JavaFX – Exemplo: Menus
Objetivo: Alternar entre painéis, consoante a opção do Menu selecionada.
Solução: Usar o layout BorderPane para a Janela. Definir dois painéis (panelA e panelB), colocar na zona central
o panelA ou o panelB consoante a opção do menu.

public class PanelWithMenu extends BorderPane {

private final PanelA panelA; Preparação do layout:


private final PanelB panelB;
1. Usar o layout BorderPane
para a Janela.
public PanelWithMenu() {
2. Declarar dois painéis como
this.panelA = new PanelA(); atributos e criá-los no
this.panelB = new PanelB(); construtor.

3. Criar o Menu Configurar e os


Menu setupMenu = new Menu("Configurar"); MenuItem
MenuItem setupMenuA = new MenuItem("Adicionar A");
MenuItem setupMenuB = new MenuItem("Adicionar B"); 4. Criar o MenuBar e associá-lo
ao topo
MenuBar menuBar = new MenuBar();
...
this.setTop(menuBar);
}
}
20
JavaFX – Exemplo: Menus
public class PainelWithMenu extends BorderPane {

private final PanelA panelA; Preparação do Funcionamento:


private final PanelB panelB;
1. Adicionar um evento ao
public PanelWithMenu() { MenuItem setupMenuA, que
this.panelA = new PanelA(); irá mostrar o panelA.
this.panelB = new PanelB(); 2. Adicionar um evento ao
MenuBar menuBar = new MenuBar(); MenuItem setupMenuB, que
this.setTop(menuBar); irá mostrar o panelB.
Menu setupMenu = new Menu("Configurar");
MenuItem setupMenuA = new MenuItem("Adicionar A");
MenuItem setupMenuB = new MenuItem("Adicionar B");
setupMenuA.setOnAction(e -> showPanelA());
setupMenuB.setOnAction(e -> showPanelB());
// (…)
}
public final void showPanelA() {
this.setCenter(panelA);
}

public final void showPanelB() {


this.setCenter(panelB);
}
} 21
JavaFX- Eventos : Exemplo

22
CheckBox e
RadioButton
▸ JavaFX – Controlos II

23
JavaFX – Exemplo: CheckBox

CheckBox

• Uma CheckBox pode estar em três estados:

• Determinado e selecionada.

• Determinada e não selecionada.

• Indeterminado

24
JavaFX – Exemplo: CheckBox
Objetivo: Mostrar um botão com um icon sempre que a
CheckBox sassociada está selecionada.

Passo 1
1. Definir os vários arrays
2. Definir as imagens a mostrar
3. Definir as CheckBox

String[] names = new String[]{"Security", "Project", "Chart"};


Image[] images = new Image[names.length];
ImageView[] icons = new ImageView[names.length];
CheckBox[] cbs = new CheckBox[names.length];
for (int i = 0; i < names.length; i++) {
Image image = images[i]
= new Image(getClass().getResourceAsStream(names[i] + ".png"));
ImageView icon = icons[i] = new ImageView();
CheckBox cb = cbs[i] = new CheckBox(names[i]);
}

25
JavaFX – Exemplo: CheckBox
Passo 2
Definir a ação a realizar quando se altera a
seleção de uma CheckBox recorrendo à
utilização de “listeners” da propriedade selected

for (int i = 0; i < names.length; i++) {


CheckBox cb = cbs[i];
ImageView icon = icons[i];
Image image = images[i];
cb.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue ov,
Boolean old_val, Boolean new_val) {
icon.setImage(new_val ? image : null);
}
});
}

26
JavaFX – Exemplo: Radio Buttons

RadioButton

ToogleGroup

• Um RadioButton pode estar selecionado ou não selecionado.


• De forma a podermos ter um grupo de botões a trabalhar “sincronizados”
só um poderá estar selecionado de cada vez
• para implementarmos escolhas exclusivas) temos que definir um
ToogleGroup.

27
JavaFX – Exemplo: Radio Buttons
Objetivo: Ter uma aplicação que em função do
RadioButton selecionado, muda a cor do texto
apresentado.

Passo 1
1. Definir os RadioButton
2. Definir o ToogleGroup
3. Associar os RadioButton ao ToogleGroup
4. Criar o Text

RadioButton yellowButton = new RadioButton("amarelo");


RadioButton blueButton = new RadioButton("azul");
RadioButton redButton = new RadioButton("vermelho");
final ToggleGroup group = new ToggleGroup();
yellowButton.setToggleGroup(group);
blueButton.setToggleGroup(group);
redButton.setToggleGroup(group);

final Text text = new Text("radio buttons");


text.setFill(Color.BLUE);
buttonAzul.setSelected(true);
28
JavaFX – Exemplo: Radio Buttons

Passo 2
Definir as ações a realizar quando existe um
evento nos botões. Uma ação para cada botão.

yellowButton.setOnAction(e -> texto.setFill(Color.YELLOW));

blueButton.setOnAction(e -> texto.setFill(Color.BLUE));

redButton.setOnAction(e -> texto.setFill(Color.RED));

29
JavaFX- Eventos : Exemplo

30
Resumindo
▸ ListView e ComboBox
▹ Permitem apresentar e manipular coleções de elementos.
▹ A ListView permite seleção Múltipla ou Simples.
▹ A ComboBox só permite a seleção de um elemento (Simples).

▸ Tab
▹ Através de um controlo TabPane, podemos implementar a alternância entre vários painéis.
▹ Associando um painel a um Tab e por sua vez os vários Tabs a um TabPane.

▸ Accordion
▹ Através de um controlo Accordion, também é possível implementar a alternância entre painéis.
▹ Criando os vários TitledPane e associando-os ao Accordion.

▸ Menu
▹ É possível definir uma hierarquia de menus e submenus, usando as classes MenuBar, Menu, MenuItem,
CheckMenuItem, RadioMenuItem, SeparatorMenuItem.

▸ CheckBox
▹ Através de um controlo CheckBox, podemos implementar a seleção de uma opção.

▸ RadioButton
▹ Para implementar a escolha exclusiva através de RadioButton, temos que definir um ToggleGroup e associar cada
RadioButton ao grupo criado. 31
Leitura Complementar
▸ Chapter 4 – Layouts and UI Controls Pgs 101 a 108
http://docs.oracle.com/javase/8/javafx/user-interface- tutorial/combo-
box.htm#BABJCCIB
http://docs.oracle.com/javase/8/javafx/user-interface-
tutorial/menu_controls.htm#BABGHADI
https://docs.oracle.com/javafx/2/ui_controls/checkbox.htm
http://docs.oracle.com/javase/8/javafx/user-interface- tutorial/radio-
button.htm#BABBJBDA

▸ Controlos UI
https://docs.oracle.com/javafx/2/ui_controls/overview.htm

32
Programação Orientada por Objetos

JavaFX – Janelas
e Formas

Prof. Cédric Grueau


Prof. José Sena Pereira
Departamento de Sistemas e Informática
Escola Superior de Tecnologia de Setúbal
Instituto Politécnico de Setúbal
2021/2022
Sumário

▸ JavaFX + CSS
▸ Exemplo

2
JavaFX – definição
de estilo com CSS
Ø JavaFX – utilização de estilo com CSS
Cascadind Style Sheets - CSS

▸ CSS é a linguagem usado para definir o estilo de um documento HTML.


▸ O CSS aplicado ao Java FX segue exatamente as mesmas regras de uma interface web
clássica, a única coisa que muda é o nome das propriedades que estão disponíveis.
▸ Na maioria das vezes, essas propriedades são as mesmas de para uma interface da web,
mas acrescenta-se o prefixo -fx-. Portanto, podemos ter:
▹ -fx-background-color-
▹ -fx-color-
▹ -fx-font-size –
▹ -fx-font-family–
▹ -fx-opacity
▹ ... 4
Estilo por defeito: modena.css

▸ O css por defeito para todas as aplicações JavaFX é escrito num ficheiro
chamado modena.css, que pode ser encontrado no ficheiro jar do JavaFX
runtime, jfxt.jar, localizado na pasta de instalação do Java.

▸ Este ficheiro css define os estilos para o nó raiz e os controles da IU.

5
Atribuição de estilos aos nós

▸ Java FX trabalha com uma noção de hierarquia de componentes, portanto, se uma


folha de estilo for aplicada ao componente mais alto nesta hierarquia, esta folha se
aplicará a todos os componentes abaixo (falamos de nós).
▸ Por outro lado, também é possível atribuir uma folha de estilo apenas a um nó muito
específico de uma cena Java FX, é ainda possível atribuir várias folhas de estilo a um
nó.

6
Propriedade de um componente: ID
▸ Assim como numa interface web, as propriedades CSS são Ficheiro .java
aplicadas em seletores que podem ser:
button = new Button("Conta");
▹ o id: dá um nome particular a um nó. button.setId("account");

▹ Um nó pode ter apenas um e apenas um ID.


▹ é possível definir um identificador Ficheiro .css
manualmente para um componente usando o
método setId ("name-of-my- #account {
-fx-background-color: transparent;
identifier").
-fx-text-fill: #ffffff;
▹ Quando usados em folhas de estilo, esses -fx-font-size: 16px;
-fx-cursor: hand;
identificadores são prefixados com um
}
cardinal(#).
▹ Por exemplo, #nameValue num painel
mostra as propriedades do botão
7
Propriedade de um componente: classe
▸ uma classe: Ficheiro .java
▹ identificador predefinido correspondente a um
componente, prefixado por um ponto. BorderPane header = new BorderPane();
▹ Por exemplo, há uma classe header-section
...
header.getStyleClass().add("header-section");
que representa as propriedades CSS de da
secção do cabeçalho.
▹ É fácil encontrar as classes de cada componente Ficheiro .css
porque existe uma convenção de nomenclatura:
.header-section {
ponto seguido do nome do componente em -fx-padding: 10px;
minúsculas e, se o nome do componente for -fx-font-size: 20px;
composto, acrescentamos um hífen. -fx-background-color:
▹ Por exemplo, temos .label, . Button, . teal;
Check-box, . Radio-button, etc
}

8
Propriedade de um componente: pseudo classe
▸ uma pseudo classe: é uma indicação que permite especificar quando deve ser aplicado o
estilo (ao passar o rato sobre o botão, ao clicar, ao clicar nele, ...).
▸ Essa indicação pode ser usada em classes e também em identificadores.
▸ Ele é concatenado a uma classe.
▹ Exemplo: .Button:focus, .Button:hover , #valueName:hover.

9
JavaFX + CSS:
Exemplo
Ø JavaFX – utilização de estilo com CSS
Layout da aplicação

▸ Modena.css demo.css

11
Estrutura da aplicação formulário
▸ Javafx-css-demo-app

javafx-css-demo-app
└ CSSDemoApplication.java
└ MainWindow.java
└ demo.css
└ javafx_account_example.png

12
JavaFX- CSS : Exemplo

13
Resumindo

▸ É fácil mudar a aparência de um componente usando um ficheiro CSS.

▸ Os nomes das propriedades CSS dos componentes Java FX são iguais aos de uma

interface web, mas são prefixados com -fx-.

▸ É possível usar pseudo-classes para modificar o estilo de um componente de acordo

com certas ações do utilizador (clique, foco, ...).

14

Você também pode gostar