Programação Orientada a Objetos com Java Módulo 06 Desenho de Classes Última Atualização: 13/06/2010 1 Programação Orientada a Objetos com Java Objetivos • Definir os conceitos de herança, polimorfismo, sobrecarga (overloading), sobreescrita(overriding) e invocação virtual de métodos. • Utilizar os modificadores de acesso protected e “packagefriendly”. • Descrever os conceitos de sobrecarga de construtores e métodos. 2 Programação Orientada a Objetos com Java Objetivos (Cont.) • Descrever a operação de construção e iniciação de um objeto. •Discutir o conceito de AutoBoxing introduzido no Java 5 3 Programação Orientada a Objetos com Java Herança A classe Empregado: public class Empregado { private Data dataNascimento; private String primeiroNome; private String ultimoNome; public String getDetalhes ( ) { ...} } 4 Programação Orientada a Objetos com Java Herança(Cont.) A classe Gerente: public class Gerente { private Data dataNascimento; private String departamento; private String primeiroNome; private String ultimoNome; public String getDetalhes ( ) { ...} } 5 Programação Orientada a Objetos com Java Herança(Cont.) public class Empregado { private Data dataNascimento; private String primeiroNome; private String ultimoNome; public String getDetalhes ( ) { ...} } public class Gerente extends Empregado { private String departamento; } 6 Programação Orientada a Objetos com Java Herança Simples • Quando uma classe herda apenas de uma outra classe, obtemos uma herança simples. • Herança Múltipla é realizada através do conceito de Interfaces (próximo módulo). • Sintaxe de uma classe Java: <modificador de acesso> class <nome> [extends <superclasse>]{ <declarações>* } 7 Programação Orientada a Objetos com Java Herança Simples(Cont.) 8 Programação Orientada a Objetos com Java Controle de Acesso Modificador Classe Pacote Subclasse private Sim default Sim Sim protected Sim Sim Sim* public Sim Sim Sim Aplicação Sim * Para uma subclasse fora do pacote, o membro protected somente pode ser acessado via herança. Se tentarmos acessar o membro via uma referência da superclasse, teremos um erro de compilação!!! 9 Programação Orientada a Objetos com Java Sobreescrita de Métodos • Uma subclasse pode modificar o comportamento herdado da super classe. • Uma subclasse pode criar um método com uma funcionalidade diferente da super classe, entretanto, com o mesmo: • • • Nome Tipo de Retorno Lista de Argumentos 10 Programação Orientada a Objetos com Java Sobreescrita de Métodos public class Empregado { protected String primeiroNome; protected double salario; protected Data dataNascimento; public String getDetalhes() { return “Primeiro Nome..:” + primeiroNome\n” + “Salario..:”+ salario + “\n” ) } public class Gerente extends Empregado { private String departamento; public String getDetalhes() { return “Primeiro Nome..:” + primeiroNome\n” + “Salario..:”+ salario + “\n” + “Departamento..:” + departamento; }} 11 Programação Orientada a Objetos com Java A Palavra Reservada super • super é utilizada em uma classe para referenciar a superclasse. • super é utilizada para referenciar membros da superclasse, mais especificamente, atributos, construtores e métodos. • O comportamento (método) invocado não necessariamente necessita estar na superclasse; ele pode estar em outro local dentro da hierarquia. 12 Programação Orientada a Objetos com Java A Palavra Reservada super public class Empregado { private String nome; private double salario; private String dataNascimento; public String getDetalhes() { return "Nome: " + nome + "\n Salário: " + salario; } } public class Gerente extends Empregado { private String departamento; public String getDetails() { // chama o método getDetalhes( ) presente na superclasse return super.getDetalhes() + "\n Departamento:" + departamento; }} 13 Programação Orientada a Objetos com Java Polimorfismo • Polimorfismo é a capacidade de se ter várias formas distintas; por exemplo, a classe Gerente tem acesso a métodos da classe Empregado. • Um objeto possui apenas uma forma. • Uma variável de referência pode referenciar objetos que possuem formas totalmente distintas. 14 Programação Orientada a Objetos com Java Polimorfismo(Cont.) Empregado empregado = new Gerente(); // OK /* Tentativa ilegal de atribuir um atributo de gerente. A variável é declarada como sendo do tipo Empregado, embora o objeto Gerente possua este atributo. */ empregado.departamento = “Vendas”; 15 Programação Orientada a Objetos com Java Invocação Virtual de Método • Invocação Virtual de Método: Empregado e = new Gerente(); e.getDetalhes(); • Tipo em tempo de Compilação e tipo em tempo de execução (Late Binding) 16 Programação Orientada a Objetos com Java Regras sobre Métodos Sobreescritos • Deve ter um tipo de retorno que seja idêntico ou subclasse do tipo definido no método que está sendo sobreescrito (Retorno co-variante, introduzido no Java 5). • Não pode ser menos acessível que o método que ele sobrescreve. 17 Programação Orientada a Objetos com Java Regras sobre Sobrescrita de Métodos(Cont.) public class Pai { public void fazAlgo() { } } public class Filha extends Pai { private void fazAlgo() { } } public class UsaAmbas { public void fazOutraCoisa() { Pai p1 = new Pai(); Pai p2 = new Filha(); p1.fazAlgo(); p2.fazAlgo(); //Erro!! }} 18 Programação Orientada a Objetos com Java Coleções Heterogêneas • Coleções de objetos com o mesmo tipo são chamadas de coleções homogêneas. Data[] datas = new Data[2]; datas[0] = new Data(22, 12, 1964); datas[1] = new Data(22, 7, 1964); • Coleções de objetos de tipos diferentes são conhecidas como coleções heterogêneas. Empregado [] funcionarios = new Empregado[1024]; funcionarios[0] = new Gerente(); funcionarios[1] = new Empregado(); funcionarios[2] = new Engenheiro(); 19 Programação Orientada a Objetos com Java Argumentos Polimórficos Devido ao fato de Gerente ser um Empregado: // Na classe Empregado public TaxaJuros encontraTaxaJuros(Empregado e) {} // Enquanto isso, em algum ponto da aplicação Gerente m = new Gerente(); ... ... TaxaJuros t = encontraTaxaJuros(m); 20 Programação Orientada a Objetos com Java O Operador instanceof public class Empregado extends Object public class Gerente extends Empregado public class Engenheiro extends Empregado --------------------------------------- - public void fazAlgo(Empregado e) { if (e instanceof Gerente) { // Processamento de Gerente } else if (e instanceof Engenheiro) { // Processamento de Engenheiro } else { // Processamento de outro tipo de Empregado } } 21 Programação Orientada a Objetos com Java Conversão (Casting) de Objetos • Use instanceof para testar o tipo de um objeto. • Restaure toda a funcionalidade de um objeto através de um casting (conversão de tipos). • Confira o casting utilizando as seguintes recomendações: • Conversões para cima na hierarquia são feitas implicitamente. • Conversões para baixo devem ser feitas para uma subclasse e sua checagem é feita pelo compilador. • O tipo do objeto é conferido em tempo de execução. 22 Programação Orientada a Objetos com Java Sobrecarga de Métodos • Utilizado da seguinte maneira: public void println(int i) public void println(float f) public void println(String s) • A lista de argumentos deve ser diferente. • O tipo de retorno pode ser diferente 23 Programação Orientada a Objetos com Java Sobrecarga de Construtores • Semelhante a métodos, podemos fazer a sobrecarga de construtores. • Exemplo: public Empregado(String nome, double salario, String data) public Empregado(String nome, double salario) public Empregado(String nome, String data) • A lista de argumentos deve ser diferente. • Você pode usar a referência this na primeira linha de um construtor para chamar outro construtor. 24 Programação Orientada a Objetos com Java Sobrecarga de Construtores 1 public class Empregado { 2 3 private String nome; 4 private double salario; 5 private String dataNascimento; 6 7 public Empregado(String nome, double salario, String dataNascimento) { 8 this.nome = nome; 9 this.salario = salario; 10 this.dataNascimento = dataNascimento; 11 } 12 public Empregado(String nome, double salario) { 13 this(nome, salario, null); 14 } 15 public Empregado(String nome, String dataNascimento) { 16 this(nome, 0.0, dataNascimento); 17 } 18 public Empregado(String nome) { 19 this(nome, salario); 20 } 21 } 25 Programação Orientada a Objetos com Java Construtores Não São Herdados • Uma subclasse herda todos os métodos e variáveis public ou protected da superclasse. • Uma subclasse NÃO herda o construtor da superclasse. • Existem duas maneiras de se incluir um construtor: • • Utilizando o construtor default, Escrever explicitamente um ou mais construtores. 26 Programação Orientada a Objetos com Java Chamando Construtores da Superclasse • Para se chamar um construtor da superclasse, devemos inserir uma chamada super na primeira linha do construtor. • Você pode chamar um construtor específico da superclasse através dos argumentos utilizados na chamada super 27 Programação Orientada a Objetos com Java Chamando Construtores da Superclasse (Cont.) • Se não houver uma chamada explícita this ou super no construtor, então o compilador adiciona uma chamada implícita para super(), que por sua vez, chama o construtor da superclasse sem argumentos (que pode ser o construtor default). • Se a superclasse definir construtores, mas não fornecer um construtor sem argumentos, então uma mensagem de erro é gerada. 28 Programação Orientada a Objetos com Java Chamando Construtores da Superclasse 1 public class Empregado { 2 3 private String nome; 4 private double salario; 5 private String dataNascimento; 6 7 public Empregado(String nome, double salario, String dataNascimento) { 8 this.nome = nome; 9 this.salario = salario; 10 this.dataNascimento = dataNascimento; 11 } 12 public Empregado(String nome, double salario) { 13 this(nome, salario, null); 14 } 15 public Empregado(String nome, String dataNascimento) { 16 this(nome, 0.0, dataNascimento); 17 } 18 public Empregado(String nome) { 19 this(nome, 0.0); 20 } } 29 Programação Orientada a Objetos com Java Chamando Construtores da Superclasse(Cont.) 1 public class Gerente extends Empregado { 2 private String departamento; 3 4 public Gerente(String nome,double salario, String dept) { 5 super(nome, salario); 6 departamento = dept; 7 } 8 public Gerente(String n, String dept) { 9 super(nome); 10 departamento = dept; 11 } 12 public Gerente(String dept) { // erro: ausência de super() explícito 13 departamento = dept; 14 } 15 } 30 Programação Orientada a Objetos e Linguagem de Programação Java Construindo e Iniciando Objetos: Breve Revisão • A memória é alocada e iniciação “default” ocorre. • A iniciação das variáveis de instância utiliza estes passos recursivamente: 1. “casa” os parâmetros do construtor. 2. Se houver um this(), chama recursivamente, e vá para o passo 5. 3. Chama super recursivamente, de forma implícita ou explícita, exceto para Object. 4. Executa iniciadores explícitos das variáveis de instância. 5. Executa o bloco do construtor. 31 Programação Orientada a Objetos e Linguagem de Programação Java Exemplo de Construtor e Iniciação public class Object { public Object() {} } public class Empregado extends Object { private String nome; private double salario = 1500.00; private String dataNascimento; public Empregado(String n, String dataNascimento) { // super() implícito nome = n; this.dataNascimento = dataNascimento; } public Empregado(String n) { this(n, null); } } public class Gerente extends Empregado { private String departamento; public Gerente(String n, String d) { super(n); departamento = d; }} 32 Programação Orientada a Objetos e Linguagem de Programação Java Exemplo de Construtor e Iniciação Exemplo supondo a construção new Gerente(“Joe Smith”, “Vendas”) 0 Iniciação básica 0.1 Alocação de memória para o objeto Gerente 0.2 Inicia todas as variáveis de instância com seus valores default (0 ou null) 1 Chama o construtor: Gerente("Joe Smith", "Vendas") 1.1 “Casa” os parâmetros do construtor: n="Joe Smith", d="Vendas" 1.2 Não há uma chamada explícita para this() 1.3 Chama super(n) para Empregado(String n) 1.3.1 “Casa” os parâmetros do construtor: n="Joe Smith" 1.3.2 Chama this(n, null) para Empregado(String n , String dataNascimento) 1.3.2.1 “Casa” parâmetros do construtor: n="Joe Smith", dataNascimento=null 1.3.2.2 Não há uma chamada explícita para this() 1.3.2.3 Chama super() para Object() 1.3.2.3.1 Nenhum “casamento” necessário 1.3.2.3.2 Não há chamada this() 1.3.2.3.3 Não há chamada super() (Object é a raiz) 1.3.2.3.4 Não existe iniciação explícita de variáveis para Object 1.3.2.3.5 Não há chamada de método 1.3.2.4 Inicia explicitamente variáveis de Empregado: salario=1500.00; 1.3.2.5 Executa o corpo do construtor: nome="Joe Smith"; dataNascimento=null; 1.3.3 - 1.3.4 passos não necessários 1.3.5 Executa o corpo do construtor: Não há instruções em Empregado(String) 1.4 Não há iniciadores explícitos para Gerente 1.5 Executa o corpo do construtor: departamento="Vendas". 33 Programação Orientada a Objetos com Java A Classe Object • A classe Object é a super classe de todas as classes em Java. • Uma declaração de classe sem a cláusula extends utiliza, implicitamente, a instrução “ extends Object ” public class Empregado { ... } é equivalente a: public class Empregado extends Object { ... } 34 Programação Orientada a Objetos com Java O Operador == Comparado com o Método equals • O operador == determina se duas referências são idênticas (ou seja, referem ao mesmo objeto). • O método equals determina se objetos são “iguais” em conteúdo, mas não necessariamente idênticos. • A implementação do método equals em Object utiliza o operador ==. 35 Programação Orientada a Objetos com Java O Operador == Comparado com o Método equals(Cont.) • Classes definidas pelo programador podem fazer a sobreescrita do método equals(Object o) para implementar um teste de igualdade-domínio. • Nota: Devemos fazer a sobreescrita do método hashCode() se fizermos a sobreescrita do método equals(Object o). • Maiores detalhes no módulo sobre Coleções Java 36 Programação Orientada a Objetos com Java Exemplo: equals 1 public class Data{ 2 private int dia; 3 private int mes; 4 private int ano; 5 6 public Data(int dia, int mes, int ano) { 7 this.dia = dia; 8 this.mes = mes; 9 this.ano = ano; 10 } 11 12 public boolean equals(Object o) { 13 boolean resultado = false; 14 if ( (o != null) && (o instanceof Data) ) { 15 Data d = (Data) o; 16 if ((dia == d.dia) && (mes == d.mes) 17 && (ano == d.ano) ) { 18 resultado = true; 19 } 20 } 21 return resultado; 22 } 23 24 public int hashCode() { 25 return (dia ^ mes ^ ano); 26 } 27 } 37 Programação Orientada a Objetos com Java Exemplo: equals 1 class TesteEquals { 2 public static void main(String[] args) { 3 Data data1 = new Data(14, 3, 1976); 4 Data data2 = new Data(14, 3, 1976); 5 6 if (data1 == data2 ) { 7 System.out.println("data1 idêntica a data2"); 8 } else { 9 System.out.println("data1 não é identica a data2"); 10 } 11 12 if (data1.equals(data2) ) { 13 System.out.println("data1 é igual a data2"); 14 } else { 15 System.out.println("data1 não é igual a data2"); 16 } 17 18 System.out.println("atribui data2 = data1;"); 19 data2 = data1; 20 21 if (data1 == data2 ) { 22 System.out.println("data1 é idêntica a data2"); 23 } else { 24 System.out.println("data1 não é identica a data2"); 25 } 26 } 27 } Gera a saída: data1 não é idêntica a data2 data1 é igual a data2 Atribui data2 = data1; data1 é idêntica a data2. 38 Programação Orientada a Objetos com Java O Método toString() • Converte um objeto em uma String. • Usado durante concatenação de strings. • Fazemos a sobreescrita deste método para fornecer informação sobre um objeto definido pelo programador em um formato legível. • Tipos primitivos são convertidos em String utilizando o método estático toString() da classe empacotadora (wrapper class). 39 Programação Orientada a Objetos com Java Classes Empacotadoras “Wrapper “ • Tratam elementos de tipos primitivos como objetos: Tipo Primitivo Classe Empacotadora boolean Boolean byte Byte short Short int Integer long Long float Float double Double char Character 40 Programação Orientada a Objetos com Java Classes Empacotadoras • Pré Java 5 // Definindo um tipo primitivo int primitivoInt = 500; // Empacotando o tipo primitivo definido acima Integer empacotandoInt = new Integer(primitivoInt); // Convertendo novamente para primitivo int primitivo2 = empacotandoInt.intValue(); • Pós Java 5 • Autoboxing • Elimina o trabalho do programador de ter que fazer o encapulamento/desencapsulamento de um tipo primitivo com sua respectiva classe empacotadora. 41 Programação Orientada a Objetos com Java Autoboxing e Unboxing 42 Programação Orientada a Objetos com Java AutoBoxing e UnBoxing - Agenda Convertendo Tipos Primitivos para Classes Empacotadoras – Wrapper Classes Convertendo Classes Empacotadoras para Tipos Primitivos Incrementando e Decrementando Classes Empacotadoras Valores Boolean versus boolean Estruturas Condicionais e UnBoxing Instruções de Controle e Unboxing Resolução na Sobrecarga de Métodos 43 Programação Orientada a Objetos com Java Autoboxing/Unboxing de tipos Primitivos Problema: (pré-Java 5): 1. Conversão entre tipos primitivos e tipos wrapper (e vice-versa) 2. Você precisa converter manualmente um tipo primitivo para um tipo wrapper antes de adicioná-lo na coleção int i = 22; Integer[] b = new Integer[1]; b[0] = new Integer(i); 44 Programação Orientada a Objetos com Java Autoboxing/Unboxing de tipos Primitivos Solução: Deixe o compilador fazer isto Byte byteObj = 22; // Conversão Autoboxing int i = byteObj // Conversão Unboxing Integer[] lista = new Integer[1]; lista[0] = 22; // Conversão Autoboxing 45 Programação Orientada a Objetos com Java Incrementando e Decrementando Classes Empacotadoras Podemos utilizar as operações ++ e - - com classes empacotadoras Integer contador = 1; while (true) { System.out.println(“Iteração..: “ + contador++); if (contador > 1000) { break; } } A variável contador é tratada com um tipo primitivo int neste código 46 Programação Orientada a Objetos com Java Incrementando e Decrementando Classes Empacotadoras (cont.) Pontos importantes neste código: contador++ ■ lembre-se que contador é um Integer. Nessa linha foi feito um Unboxing para o tipo int que é o tipo necessário ao operador ++ (o operador ++ não foi alterado para funcionar com Unboxing) ■ Após unboxing do valor, o incremento é feito e o novo valor é armazenado novamente em contador através de uma operação Boxing. ■ O processo de unboxing também é demonstrado na linha contador > 1000 47 Programação Orientada a Objetos com Java Valores Boolean versus boolean Operadores booleanos como !, ||, && são úteis para valores Boolean. Sempre que tivermos uma expressão que usa !, ||, ou && qualquer valor Boolean é convertido em tipo primitivo e avaliado adequadamente Boolean caso1 = true; Boolean caso2 = true; boolean caso3 = false; Boolean resultado = (caso1 || caso2) && caso3; Neste código o resultado da expressão, é encapsulado dentro da variável resultado (AutoBoxing) 48 Programação Orientada a Objetos com Java Estruturas Condicionais e UnBoxing O conceito de boxing/unboxing afeta, tornando mais flexível, a utilização do operador ternário expressao ? expressao1 : expressao2; Em versões anteriores ao Tiger o operador ternário necessitava que seus operandos fossem de tipos compatíveis ou que um pudesse ser associado a outro (ex: int e float). No tiger, os operandos do operador ternário podem ser praticamente de qualquer tipo. 49 Programação Orientada a Objetos com Java Estruturas Condicionais e UnBoxing Exemplo: Boolean estaChegando = false; Boolean estaAtrasado = true; System.out.println(estaChegando? “chegou no horário” : “Você está atrasado”); O operador ternário no tiger permite o casting automático de referência para a superclasse comum aos dois operandos. Exemplo (Funciona somente a partir do JavaSE 5, Tiger) : String s = olá; StringBuffer sb = new StringBuffer(“mundo”); boolean eMutavel = true; CharSequence c = eMutavel? sb : s; // CharSequence é superclasse de // StringBuffer e String 50 Programação Orientada a Objetos com Java Estruturas Condicionais e UnBoxing (Cont.) O código no slide anterior gera um erro em versão anteriores ao Java 5 já que String e StringBuffer não podem ser associadas uma a outra. Este código poderá funcionar em versões anteriores ao Java 5 se o programador fizer algumas conversões: String s = olá; StringBuffer bf = new StringBuffer(“mundo”); boolean eMutavel = true; CharSequence c = eMutavel? (CharSequence)sb : (CharSequence)s; Repare que poderíamos utilizar Object em vez de CharSequence no exemplo acima. 51 Programação Orientada a Objetos com Java Estruturas de Controle e UnBoxing As estruturas de controle if/else, while, do são afetadas pelo conceito de boxing/unboxing Integer pessoasNaSala = 0; int capacidadeMaxima = 100; boolean horaDePartir = false; Boolean chegando = true; while (pessoasNaSala < capacidadeMaxima) { if (chegando) { System.out.println("Bom ver você."); pessoasNaSala++; } else { pessoasNaSala--; } if (horaDePartir) { do { System.out.printf("Número de pessoas na sala ” + pessoasNaSala); pessoasNaSala--; } while (pessoasNaSala > 0); } } 52 Programação Orientada a Objetos com Java Estruturas de Controle e UnBoxing (Cont.) Outra estrutura de controle beneficiada é o switch Em Tiger a instrução switch funciona com: ■ byte, short, int, char, Byte, Short, Integer, Character e enum 53 Programação Orientada a Objetos com Java Resolução na Sobrecarga de Métodos Resolução de métodos é o processo pelo qual o compilador Java determina qual método é invocado. Boxing e Unboxing afetam este processo. Exemplo: public void fazAlgo(double num) public void fazAlgo(Integer num) Invocando fazAlgo() : ... int foo = 1; fazAlgo(foo); // Qual método é chamado ??? ... 54 Programação Orientada a Objetos com Java Resolução na Sobrecarga de Métodos (Cont.) Antes do Tiger isso era fácil: O tipo int era convertido em double e o método fazAlgo(double num) era invocado A primeira vista pode parecer que, com o Tiger, o tipo int é encapsulado em um Integer e faz Algo(Integer num) é invocado ISSO NÃO ACONTECE !!!! Resolução de métodos em Tiger sempre selecionará o mesmo método que seria invocado utilizando Java 4 (Compatibilidade retroativa) 55 Programação Orientada a Objetos com Java Perguntas? 56