Programação por Objectos Java Parte 5: Associações LEEC@IST Java – 1/32 Associação – revisão • Uma associação representa uma referência entre objectos. • Numa associação são definidos: – Identificador – termo descritivo da associação. – Papeis (role) representados pela associação em cada uma das classes relacionadas. – Multiplicidade – número de objectos associados em cada instância da associação. LEEC@IST Java – 2/32 Associação (1) • Associação: representada por atributos de tipo classe do associado. 1. A associação é definitivamente estabelecida quando ambos os atributos forem inicializados. 2. A eliminação da associação exige que os dois atributos deixem de referenciar o outro objecto associado. LEEC@IST Java – 3/32 Associação (2) Pessoa 1 dono public class Pessoa { Conta conta; ... Pessoa(){ conta = null; ... } void associarConta(Conta conta) { this.conta = conta; } } LEEC@IST 0..1 Conta conta public class Conta{ Pessoa dono; ... Conta(Pessoa dono){ this.dono = dono; ... } } Java – 4/32 Associação (3) • Associações dirigidas: no Java, apenas a classe partida da seta contém o atributo para a classe de chegada. • Multiplicidade das associações: no Java, a multiplicidade distinta de 0..1 e 1 é implementada por atributos de tipo tabelas, Vector, … nas classes associadas. • Associações que transportam informação: no Java, as classes associadas possuem um atributo extra para a classe de associação. LEEC@IST Java – 5/32 Associação (4) Pessoa reside public class Pessoa { Conta conta; Endereço reside; ... void Pessoa(Endereço endereço) { conta = null; reside = endereço; ... } } LEEC@IST Endereço public class Endereço { String rua; ... } Java – 6/32 Associação (5) Pessoa 1 dono 0..10 Conta contas public class Pessoa { public class Conta{ int numProxConta=0; Pessoa dono; static final int numMaxConta=10; ... Conta[] conta; Conta(Pessoa dono){ ... this.dono = dono; Pessoa() { conta = new Conta[numMaxConta]; } } } void associarConta(Conta conta){ if (numProxConta<numMaxConta) this.conta[numProxConta++] = conta; else System.out.println(“Número máximo atingido!"); } } LEEC@IST Java – 7/32 Associação (6) Pessoa 1..100 1 empregado Empresa empregador Emprego ~ vencimento: float ~ dataEntrada: long public class Pessoa { Empresa empregador; Emprego emprego; ... } LEEC@IST public class Emprego { float vencimento; long dataEntrada; ... } Java – 8/32 Associação (7) public class Empresa { int numProxContrato=0; static final int numMaxContrato=100; Object[][] ee; // par(empregado,emprego) ... Empresa() { ee = new Object[numMaxContrato][2]; ... } void novoContrato(Pessoa empregado, Emprego emprego) { if (numProxContrato<numMaxContrato) { ee[numProxContrato][0]=(Object)empregado; ee[numProxContrato++][1]=(Object)emprego; } else System.out.println(“Número máx atingido!”); } } LEEC@IST Java – 9/32 Associação (8) • É da responsabilidade do programador garantir o correcto estabelecimento das associações e manutenção da sua coerência. LEEC@IST Java – 10/32 Classes aninhadas (1) • Uma classe aninhada é uma classe definida dentro de outra classe. • Uma classe aninhada é um membro da classe que a envolve: – A classe aninhada e a classe que a envolve partilham uma relação de confiança em que cada uma pode aceder a todos os membros da outra (privados ou não). – As classes aninhadas podem ter qualificadores de visibilidade próprios dos membros de classe (private, protected, public, ou por omissão, de pacote). LEEC@IST Java – 11/32 Classes aninhadas (2) • Uma classe aninhada pode estender qualquer outra classe, incluindo a classe que a envolve. • Qualquer classe, com excepção da classe que a envolve, pode estender a classe aninhada, desde que tenha acesso à mesma, contudo a subclasse não herda os privilégios de acesso que a classe aninhada tem com a classe que a envolve. • Tal como qualquer classe não aninhada, uma classe aninhada pode ser declarada final ou abstract. LEEC@IST Java – 12/32 Classes aninhadas (3) • Uma classe aninhada pode ou não ser estática: – Uma classe aninhada estática permite estruturar tipos em contextos onde os tipos façam sentido. – Uma classe aninhada não estática define uma relação especial entre o objecto aninhado e o objecto que o envolve. LEEC@IST Java – 13/32 Classes aninhadas estáticas (1) • Quando uma classe aninhada é estática, comportase como uma classe não aninhada, excepto que: – O seu nome é definido pela classe envolvente (expresso na forma ClasseEnvolvente.ClasseAninhada). – A sua acessibilidade é definida pela classe envolvente: a classe aninhada é acessível apenas se a classe envolvente é acessível. – Uma classe aninhada pode aceder a todos os membros da classe que a envolve (privados ou não), incluindo membros herdados (a membros não estáticos apenas através de uma referência apropriada). LEEC@IST Java – 14/32 Classes aninhadas estáticas (2) public class Conta { private long quantia; private Pessoa dono; public static class Permissão { public boolean levantar, fechar; } public Permissão permissãoPara(Pessoa p) { Permissão perm = new Permissão(); perm.levantar = podeLevantar(p); perm.fechar = podeFechar(p); return perm; } // ... definir podeLevantar e podeFechar } LEEC@IST Java – 15/32 Classes aninhadas estáticas (3) • Relativamente ao exemplo anterior: – Num objecto mc do tipo Conta, a permissão para o dono da conta pode ser obtida da seguinte forma: • Conta.Permissão perm = mc.permissãoPara(dono); – O javac gera os seguintes ficheiros: • • LEEC@IST Conta.class Conta$Permissão.class Java – 16/32 Classes aninhadas não estáticas (1) • Quando uma classe aninhada não é estática, comporta-se como uma classe não aninhada, excepto que: – Não pode conter membros estáticos (incluindo classes aninhadas estáticas), com a excepção de atributos simultaneamente final e static. • Uma instância de uma classe aninhada não estática está associada a uma instância da classe que a envolve. LEEC@IST Java – 17/32 Classes aninhadas não estáticas (2) public class Conta { private long número; private long quantia; private Pessoa dono; private Acção últimaAcção; public class Acção { private String acção; private long quantia; Acção(String acção, long quantia) { this.acção = acção; this.quantia = quantia; } public String toString() { //identifica a conta que a envolve return número+": "+acção+" "+quantia; } } LEEC@IST Java – 18/32 Classes aninhadas não estáticas (3) // ... Continuação do slide anterior public void depositar(long quantia) { this.quantia += quantia; últimaAcção = new Acção("depositar", quantia); } public void levantar(long quantia) { this.quantia -= quantia; últimaAcção = new Acção(“levantar", quantia); } // ... } LEEC@IST Java – 19/32 Classes aninhadas não estáticas (4) • Relativamente ao exemplo anterior: – A instrução que cria a última acção no depósito e levantamento é equivalente a: • • últimaAcção = this.new Acção("depositar", quantia); últimaAcção = this.new Acção("levantar", quantia); – O método toString acede directamente ao atributo número da classe Conta que o envolve. • • LEEC@IST Uma classe aninhada pode aceder directamente a todos os membros da classe que a envolve, incluindo atributos e métodos privados. A classe envolvente também pode aceder aos membros privados da classe aninhada, mas apenas através de uma referência explícita para o objecto da classe aninhada (últimaAcção). Java – 20/32 Classes aninhadas não estáticas (5) • Relativamente ao exemplo anterior (cont): – Um objecto da classe aninhada está sempre associado a um objecto da classe que o envolve, contudo o recíproco não é verdadeiro. • Uma classe envolvente pode não ter associado um objecto da classe aninhada, ou pode ter vários. • Quando o método depositar cria um objecto Acção, uma referência para o objecto Conta com quem está associado é automaticamente guardada no novo objecto Acção. – O nome da referência para o objecto envolvente é o this precedido pelo nome da classe envolvente, uma forma de thisqualificado. – Alternativamente, o método toString podia aceder ao número do objecto Conta com quem está associado da seguinte forma: » return Conta.this.número+": "+acção+" "+quantia; LEEC@IST Java – 21/32 Classes aninhadas não estáticas (6) • Relativamente ao exemplo anterior (continuação): – O javac gera os seguintes ficheiros: • Conta.class • Conta$Acção.class LEEC@IST Java – 22/32 Classes aninhadas não estáticas (7) • Um método de transferência de uma certa quantia de uma outra conta para a conta actual… public void transferir(Conta outra, long quantia) { outra.levantar(quantia); depositar(quantia); últimaAcção=this.new Acção("transferir-d",quantia); outra.últimaAcção=outra.new Acção("transferir-l",quantia); } LEEC@IST Java – 23/32 Classes aninhadas não estáticas (8) • Os atributos e métodos de uma classe aninhada podem esconder alguns atributos e métodos da classe que a envolve de duas formas distintas: – Um atributo ou método, declarado na classe envolvente, é redeclarado na classe aninhada. – Um atributo ou método, declarado na classe envolvente, é herdado pela classe aninhada. • • Em ambos os casos, o uso do nome simples na classe aninhada é sempre relativo à classe aninhada. Para se aceder ao atributo ou método da classe envolvente deve usar-se explicitamente o this-qualificado. LEEC@IST Java – 24/32 Associação (9) • As associações podem ainda ser implementadas com recurso às classes aninhadas (ver slide 31 de agregação/composição (4)). LEEC@IST Java – 25/32 Agregação/Composição – revisão (1) • A agregação/composição é uma associação, que denota uma relação do todo ser formado por partes. • A agregação/composição é dita como sendo uma relação de “has-a”. LEEC@IST Java – 26/32 Agregação/Composição – revisão (2) • A agregação é uma associação, que denota uma relação do todo ser formado por partes. todo parte • Na composição, o desaparecimento do todo conduz ao desaparecimento das partes. todo LEEC@IST parte Java – 27/32 Agregação/Composição (1) • A agregação/composição pode ser implementada em Java tal como uma associação: – Atributos de tipo referência. – Classes aninhadas. • Relativamente à composição, é necessário ainda garantir a coerência sobre o desaparecimento do todo implicar a desaparecimento das partes. LEEC@IST Java – 28/32 Agregação/Composição (2) 1 Empresa 1..10 Departamento public class Empresa { int numProxDepartamento = 0; static final int numMaxDepartamentos = 10; Departamento departamentos[]; ... Empresa() { departamentos = new Departamento[numMaxDepartamentos]; ... } LEEC@IST Java – 29/32 Agregação/Composição (3) // ... Continuação do slide anterior void novoDepartamento() { if (numProxDepartamento<numMaxDepartamentos) departamentos[numProxDepartamento++] = new Departamento(this); else System.out.println(“Número máx atingido!”); } } public class Departamento { Empresa empresa; ... Departamento(Empresa e) { empresa = e; } } LEEC@IST Java – 30/32 Agregação/Composição (4) • Alternativamente, usando classes aninhadas: – A classe Departamento é definida como classe aninhada da classe Empresa. – Deixa de ser necessário guardar uma referência da classe Departamento para a classe Empresa, pois esta é disponibilizada implicitamente pelo Java através do thisqualificado. LEEC@IST Java – 31/32 Agregação/Composição (5) public class Empresa { int numProxDepartamento = 0; static final int numMaxDepartamentos = 10; Departamento departamentos[]; ... public class Departamento {...} Empresa() { departamentos = new Departamento[numMaxDepartamentos]; ... } void novoDepartamento() { if (numProxDepartamento<numMaxDepartamentos) departamentos[numProxDepartamento++] = new Departamento(); else System.out.println(“Número máx atingido!”); } LEEC@IST Java – 32/32