if669 - Introdução à Programação Aula 4 Igor Ebrahim (ies) Monitoria de IP Agenda • • • • • • Encapsulamento Conversão de Tipos Interfaces Herança Polimorfismo Genéricos Encapsulamento • Visões de objetos: – Interna - atributos e métodos da classe que o define; – Externa - os serviços que um objeto proporciona e como ele interage com o resto do sistema; • Externamente, um objeto é uma entidade encapsulada; • Um objeto pode usar os serviços providos por outro – mas não deve saber como estes serviços são implementados; • Qualquer mudança no estado de algum atributo do objeto usado deve ser feito através de seus métodos – dificultar (tornar impossível) a mudança direta de atributos fora da própria classe; Encapsulamento • Pode-se pensar no objeto encapsulamento como sendo uma caixa preta; Cliente Métodos Atributos Modificadores de Acesso • Java torna possível o encapsulamento através dos modificadores de acesso; • Um modificador é uma palavra reservada que especifica uma característica particular de um método ou atributo; • Java possui três modificadores de acesso: public, protected e private; Modificadores de Acesso • Membros que são declarados como: – public podem ser referenciados em qualquer parte; – private só podem ser referenciados dentro da própria classe; • Membros que são declarados sem modificadores de acesso têm visibilidade default e podem ser referenciados por qualquer classe do mesmo pacote; Modificadores de Acesso Variáveis Métodos public private Viola o encapsulamento Força o encapsulamento Oferece serviços aos clientes Dá suport a outros métodos da classe Modificadores de Acesso • Por possuir atributos privados, a classe geralmente oferece serviços através de seus métodos para que se acessar e alterar seus valores. • Esses métodos são conhecidos como Getters e Setters. • Exemplo: – Se tivermos o atributo private int altura. – O método de acesso será: public int getAltura(). – O método para mudança será: public void setAltura(int altura). Modificadores de Acesso • O uso desta prática dá ao programador o direito de restringir a forma como um atributo pode ser mudado; • Por exemplo: – Imagine uma classe Dado que possui o atributo: private int face; – Uma face de um dado não tem que estar entre 1 e 6, então, o método setFace ficaria assim: public void setFace(int face) { if (face >= 1 && face <=6) this.face = face; } Conversão de Tipos • Pode ser conveniente converter um tipo de dado para outro; • Por exemplo, em uma situação particular podemos querer tratar um inteiro como um ponto flutuante; • Essas conversões não mudam o tipo da variável ou o valor armazenado, elas apenas convertem o valor como parte da computação; Conversão de Tipos • Conversões devem ser feitas com cuidado para evitar perda de informação; • Conversão Ampliada são mais seguras porque elas tendem a ir de um tipo “menor” para um “maior”; • Conversão Reduzida podem perder informação porque vão de um tipo “maior” para um “menor”; • Em Java, conversão de dado pode ocorrer de três tipos: – Conversão atribuída; – Promoção; – Casting. Conversão Atribuída • Conversão atribuída ocorre quando um valor de um tipo é atribuído a uma variável de outro tipo; float f; int i; i = 2; f = 2; • Apenas conversões ampliadas podem ser feitas dessa maneira; • Note que o valor e o tipo de i não mudam; Promoção • Promoção ocorre automaticamente quando operadores em uma expressão convertem seus operandos; • Por exemplo, se soma for um float e o cont for um int: result = sum / cont; • O valor de cont é convertido para ponto flutuante para realizar o cálculo; Casting • Casting é a mais poderosa, porém perigosa, técnica de conversão; • Tanto conversão ampliada e reduzida podem ser feitas; • O tipo é posto entre parênteses em frente ao valor a ser convertido; • Por exemplo, se total e cont forem inteiros: result = (float) total / cont; • O valor de result será um ponto flutuante; Interfaces • Uma interface em Java é uma coleção de métodos abstratos e constantes; • Um método abstrato é um cabeçalho de método sem o seu corpo; • Um método abstrato pode ser declarado usando o modificador abstract, mas como todos os métodos de uma interface são abstratos, normalmente não usa-se o modificador; • Uma interface é usada para estabelecer um conjunto de métodos que uma classe irá implementar; Interfaces interface é palavra reservada Nenhum dos métodos de uma interface possuem uma definição public interface Factivel { public void facaIsto(); public int facaIsto(); public void facaIsto2 (float valor, char ch); public boolean facaOutro (int num); } Um ponto-e-vírgula termina cada um dos cabeçalhos dos métodos Interfaces • Uma interface não pode ser instanciada; • Métodos em uma interface têm que ser public “por default”; • Uma classe implementa uma interface: – Declarando isto no cabeçalho da classe; – Implementando cada um dos métodos abstratos da interface. • Uma classe que implementa uma interface deve definir TODOS os métodos da interface; Interfaces public class Faz implements Factivel { public void facaIsto () implements é palavra { reservada de Java // qualquer coisa } public int facaIsto () { // qualquer coisa } // etc. } Cada método listado em Factivel recebe uma definição Interfaces • Uma classe que implementa uma interface pode definir outros métodos também; • Além de métodos abstratos, interfaces podem conter constantes; • Quando uma classe implementa uma interface, ela ganha acesso a todas essas constantes. Interfaces • Uma classe pode implementar múltiplas interfaces; • As interfaces são listadas após o implements; • A classe deve implementar todos os métodos de todas as interfaces listadas. class MuitasCoisas implements interface1, interface2 { // todos os métodos de ambas as interfaces } Interfaces • A biblioteca padrão de Java contem uma série de interfaces úteis; • A interface Comparable contém um método abstrato chamado compareTo, que é usado para comparar dois objetos; • A classe String implementa Comparable, dando a ela a habilidade de colocar strings em ordem lexicográfica; Olhem a API de Java para mais detalhes... Dêem uma olhada em na interface Iterator. Qualque dúvida procurem seus monitores. Herança • Técnica de programação orientada a objetos usada para organizar e criar classes para reuso; • Possibilita derivar uma classe de outra já existente; • A classe existente é chamada de super-classe ou classe base; • A classe derivada é chamada de sub-classe; • A sub-classe herda as características da superclasse, ou seja, a sub-classe herda os métodos e os atributos definidos pela super-classe; Herança • A herança cria uma relação “é um”, isto é, a subclasse é uma versão mais específica que a superclasse;’ Forma FormaBidimensional Circulo Quadrado Triângulo FormaTridimensional Esfera Cubo Tetraedro Herança • O programador pode implementar uma classe derivada como for preciso, adicionando novos atributos ou métodos, ou até mesmo, modificando os herdados; • Reuso de software é uma vantagem no uso de herança; Criando Sub-classes • Em Java, usa-se a palavra reservada extends para estabelecer uma relação de herança: class FormaBidimensional extends Forma { // conteúdo da classe } Modificadores de Acesso • Modificadores de acesso afetam a maneira como atributos e métodos podem ser usadas nas subclasses; • Atributos e métodos declarados privados (private) não podem ser referenciados diretamente em sub-classes; • Atributos e métodos declarados públicos (public) podem ser referenciados diretamente em subclasses – mas atributos públicos ferem o princípio do encapsulamento; Modificadores de Acesso • O modificador protected permite à sub-classe referenciar uma variável ou métodos da classe herdada diretamente; • Isso oferece um maior encapsulamento do que o uso do public, mas não tanto quanto o private; • Um atributo (ou método) protected é visível a qualquer classe dentro do mesmo pacote da super-classe; super • Construtores não são herdados, mesmo que sejam public; • Ainda assim, geralmente precisamos usar o contrutor da super-classe para inicializar a parte herdada; • A referência super pode ser usada para referenciar a classe herdada ou para invocar o seu construtor. super • O construtor da sub-classe é responsável por chamar o construtor da super-classe; • A primeira linha do construtor da sub-classe deve conter a referência super para chamar o construtor da super-classe; • A referência super também pode ser usada para referenciar outros atributos ou métodos definidos na super-classe. Herança Múltipla • Java suporta herança simples, ou seja, uma classe pode herdar apenas de uma outra classe; • Múltipla herança permite a uma classe herdar de várias outras classes; • Colisões, como duas super-classes terem o mesmo nome de um atributo, têm que ser resolvidas; • Java não suporta herança múltipla. Overriding Métodos • Uma sub-classe pode sobrescrever (override) a definição de um método herdado; • O novo método tem que possuir a mesma assinatura do método herdado, mas pode ter um corpo completamente diferente; • O tipo do objeto que executa o método é que determina qual das versões é realmente invocada; • Se um método for definido na super-classe com o modificador de acesso final, ele não pode ser sobrescrito; Overloading vs. Overriding • Overloading (sobrecarregar) múltiplos métodos com o mesmo nome e na mesma classe, mas com assinaturas diferentes; • Overriding (sobrescrever) métodos em classes diferentes (um na sub-classe e outro na superclasse) que possuem a mesma assinatura; • Overloading permite ao programador definir operações semelhantes, mas de diferentes formas e para parâmetros diferentes; • Overriding permite ao programador definir operações semelhantes, mas de diferentes formas e para diferentes tipos de objetos. A Classe Object • A classe Object é definida no pacote java.lang da biblioteca básica de Java; • Todas as classes são derivadas da classe Object; • Se uma classe não é explicitamente definida como filha de uma outra classe já existente, Java assume que ela é filha direta de Object; A Classe Object • A classe Object possui alguns métodos que podem ser úteis, que são herdados por todas as classes; • Por exemplo, o método toString é definido em Object; • Toda vez que definimos o método toString, nós estamos sobrescrevendo o método herdado; • O método toString na classe Object é definido para retornar uma String que contém o nome da classe do objeto, além de outras informações; A Classe Object • O método equals da classe Object retorna true se duas referências forem “aliases”; • Podemos sobrescrever o equals em qualquer classe para definir a igualdade de uma melhor maneira; • Para verificar a igualdade entre duas String´s, devemos usar o método equals; • Os programadores da classe String sobrescreveram o método equals herdado de Object em favor de seu melhor uso. Classes Abstratas • Uma classe abstrata é uma classe que representa uma idéia (conceito) genérica; • Uma classe abstrata não pode ser instanciada; • Usa-se o modificador abstract no cabeçalho da classe para declará-la como uma classe abstrata: public abstract class Produto { // conteúdo } Classes Abstratas • Uma classe abstrata geralmente contém métodos abstratos (sem definição); • Ao contrário das interfaces, o modificador abstract tem que ser aplicado a todos os métodos abstratos; • As classes abstratas também podem possuir métodos não abstratos; • As classes abstratas não precisam necessariamente possuir métodos abstratos. Classes Abstratas • Uma sub-classe de uma classe abstrata tem que sobrescrever seus métodos abstratos ou eles continuarão sendo considerados abstratos; • Um método abstrato não pode ser definido como final ou static; Herança para Interfaces • Herança também pode ser aplicada a interfaces da mesma forma que nas classes; • Isto é, uma interface pode derivar de uma outra já criada; • A interface filha herda todos os métodos abstratos da interface herdada; • A classe que implementar a interface filha deve definir todos os métodos; Herança • Uma relação de herança bem feita pode contribuir bastante com a elegância, a manutenção e o reuso do software; • Toda herança pode ser uma relação “é um”; • Pense sempre adiante, na potencialidade de uma classe ser herdada.; • Encontre características comuns entre as classes e empurre-as para cima na hierarquia; • Sobrescreva métodos apropriadamente; • Adicione novos atributos nas sub-classes, mas não redefina os herdados; Herança • Permita que cada classe gerencie seu próprio conteúdo; use o super para chamar o construtor da classe herdada; • Use classes abstratas para representar conceitos gerais; • Use os modificadores de acesso com cuidado para não violar o encapsulamento; Polimorfismo • Polimorfismo é um conceito de orientação objeto que nos permite criar versáteis designs de softwares Binding (Ligação) • Considere a seguinte chamada de método: obj.facaIsto(); • Em algum ponto, essa chamada é ligada à definição do método; • Em Java, esta ligação é feita dinamicamente, ou seja, em tempo de compilação; • Por isto chamamos de dynamic binding (ligação dinâmica) Polimorfismo • O termo polimorfismo significa “muitas formas”; • Uma referência polimórfica é uma variável que pode se referir a diferentes tipos de objetos em diferentes instantes; • Um método invocado através de uma referência polimórfica pode mudar de chamada para chamada; • Todas as chamadas de método em Java são potencialmente polimórfica; Polimorfismo • Suponha que criamos a seguinte variável: Emprego emp; • Java permite a esta referência apontar a um objeto Emprego ou a qualquer outro objeto de tipo compatível; • Essa compatibilidade pode ser estabelecida usando herança ou interfaces; • Um uso cuidadoso de polimorfismo pode gerar elegantes e robustos softwares; Referências e Herança • Uma referência para um objeto pode apontar para um objeto de sua própria classe ou para um objeto relacionado a ele por herança; • Por exemplo, se a classe Feriado é usada para derivar a classe Natal, então uma referência de Feriado pode apontar para um objeto Natal: Feriado Feriado dia; dia = new Natal(); Natal Polimorfismo via Herança • É o tipo do objeto que está sendo referenciado, não o tipo da referência, que determina qual método é chamado; • Suponha que o classe Feriado tenha um método chamado celebrar, e que a classe Natal sobrescreve-a; • Agora, considere a seguinte chamada: dia.celebrar(); • Se dia refere-se a um objeto Feriado, então invoca-se a versão de celebrar de Feriado; Se dia refere-se a um objeto Natal, ele chama a versão de Natal Polimorfismo via Interfaces • Um nome de uma interface pode ser usada como tipo de uma referência de uma variável de objeto: Speaker atual; • A referência atual pode ser usada para apontar para qualquer objeto de qualquer classe que implemente a interface Speaker; • A versão do método speak que a seguinte linha chama depende do tipo de objeto que atual referese: atual.speak(); Polimorfismo via Interfaces • Suponha que duas classes, Filosofo e Cao, ambas implementam a interface Speaker, fornecendo versões distintas do método speak; • Analisemos o seguinte código: Speaker s = new Filosofo(); s.speak(); s = new Dog(); s.speak(); Verificação Dinâmica de Tipo • Podemos usar Casting ou o operador instanceof para fazer verificação de tipo: • Cast: Conta conta; conta = new Poupanca("21.342-7"); ... ((Poupanca) conta).renderJuros(0.01); conta.imprimirSaldo(); Verificação Dinâmica de Tipo • instanceof: Conta c = this.procurar("123.45-8"); if (c instanceof Poupanca) ((Poupanca) c).renderJuros(0.01); else System.out.print("Poupança inexistente!“) • Casts geram exceções quando instanceof retorna false; • Casts são essenciais para verificação estática de tipos (compilação); Genéricos • Recurso novo do J2SE 5.0 (Tiger); Genéricos • Métodos e classes genéricas estão entre as capacidades mais poderosas de Java para reutilização de software com segurança de tipo em tempo de compilação; • Ou seja: – Maior facilidade de desenvolvimento; – Maior robustez: • Cria classes type-safe; • Evita o uso extensivo de type casts e instanceof. • Métodos/classes genéricas permitem que, com uma única declaração de método/classe, o programador especifique um conjunto de métodos/classes; Genéricos Date hoje = new Date(); List lista = new ArrayList(); lista.add(hoje); lista.add(“10/12/2005”); ClassCastException na 2ª iteração public void imprimirListaData(List datas){ Iterator i = datas.iterator(); while(i.hasNext()){ Date data = (Date)i.next(); System.out.println(data); } System.out.println(“A lista tem ” + datas.size() + “ datas.”); } Genéricos Date hoje = new Date(); List lista = new ArrayList(); lista.add(hoje); lista.add(“10/12/2005”); Sem Exception, mas com erro de lógica public void imprimirListaData(List datas){ Iterator i = datas.iterator(); while(i.hasNext()){ Object o = i.next(); if(o instanceof Date){ Date data = (Date)o; System.out.println(data); } } System.out.println(“A lista tem ” + datas.size() + “ datas.”); } Genéricos Date hoje = new Date(); List<Date> lista = new ArrayList<Date>(); lista.add(hoje); lista.add(“10/12/2005”); Erro de compilação public void imprimirListaData(List<Date> datas){ Iterator<Date> i = datas.iterator(); while(i.hasNext()){ Date data = i.next(); System.out.println(data); } System.out.println(“A lista tem ” + datas.size() + “ datas.”); } Métodos Genéricos • Considere três métodos sobrecarregados essese char visto Usamos Integer, Double printArray e Character ao invés de int, double métodos imprimem de ser string dos elementos de que somento tipos as porrepresentações referência podem utilizados com métodos e um array de Integers, um array de Doubles e um array de classes genéricas! Characters, respectivamente: public void printArray( Integer[] inputArray) { for (Integer element : inputArray) System.out.print(element + “ ”); } public void printArray( Double[] inputArray) { for (Double element : inputArray) System.out.print(element + “ ”); } public void printArray( Character[] inputArray) { for (Character element : inputArray) System.out.print(element + “ ”); } Métodos Genéricos • Agora, repare no método printArray genérico: Muito menos código, não?! Seção de parâmetro de tipo Cada seção de parâmetro de tipo pode conter um ou mais parâmetros de tipo separados por vírgulas. public <E> void printArray( E[] inputArray ) { for ( E element : inputArray ) System.out.print(element + “ ”); } É recomendado que parâmetros de tipos sejam especificados como letras maiúsculas individuais. Classe Genérica (ou Parametrizada) public class Registro<K,V>{ private K chave; private V valor; Registro(K chave,V valor){ this.chave = chave; this.valor = valor; } } Argumentos de tipo ... Registro<String, Integer> registro = new Registro<String, Integer>(“um”, new Integer(1)); ... Limite Inferior public class Registro<K,V extends Number>{ private K chave; private V valor; Com classe ou interface usamos a Registro(K chave,V valor){ palavra reservada this.chave = chave; extends this.valor = valor; } } //OK Registro<String, Double> registro = new Registro<String, Double >(“um”,new Double(1.0)); //Erro de compilação Registro<String, Endereco> registro = new Registro<String, Endereco >(“um”,new Endereco()); Genéricos e Coleções • Genéricos são muito utilizados com coleções; • Por exemplo, até o Java 1.4, um ArrayList era uma coleção de Object, mesmo que durante a execução, instâncias de vários tipos pudessem estar associados a ele: ArrayList listaInteiros = new ArrayList(); //OK listaInteiros.add(new Integer(2)); listaInteiros.add(new String(“2”)); //Erro execução Integer i = (Integer)listaInteiros.get(1); Genéricos e Coleções • Com genéricos: ArrayList<Integer> listaInteiros = new ArrayList <Integer>(); //OK listaInteiros.add(new Integer(2)); //Erro de compilação listaInteiros.add(new String(“2”)); //Não precisa de cast Integer i = listaInteiros.get(0); Genéricos e Coleções • Usando o novo for: ArrayList<Integer> listaInteiros = new ArrayList <Integer>(); listaInteiros.add(new Integer(1)); listaInteiros.add(new Integer(2)); for(Integer i:listaInteiros ){ out.println(i); } Curinga • Suponha que você queira implementar um método genérico sum que some os número sem uma coleção, como o ArrayList; • Queremos ser capazes de somar todos os números na ArrayList independente dos seus tipos; public double sum( ArrayList< Number > list ) { double total = 0; for ( Number element : list ) total = total + element.doubleValue(); return total; Será que esse método funcionaria se } passássemos como parâmetro um ArrayList< Integer > ? Curinga • Ao compilar o programa, o compilador emitiria a seguinte mensagem de erro: sum(java.util.ArrayList<java.lang.Number>) in NomeDaClasse cannot be applied to (java.util.ArrayList<java.lang.Integer>) • Embora Number seja superclasse de Integer, ArrayList<Number> não é superclasse de ArrayList<Integer>; • Como criar uma versão mais flexível? Usando os argumentos de tipo curinga; Curinga • Os curingas permitem especificar parâmetros de método, valores de retorno, variáveis ou campos etc., que atuam como supertipos de tipos parametrizados; Tipo desconhecido public double sum ( ArrayList< ? extends Number > list ) { double total = 0; for ( Number element : list ) total = total + element.doubleValue(); return total; } Utilizar um curinga na seção de parâmetros de tipo de um método ou utilizar um curinga como um tipo explícito de uma variável no corpo do método é um erro de sintaxe. Genéricos e Type Safety • O javac (compilador de Java) 5.0 reclamará caso encontre alguma ação insegura quanto ao tipo: List list = new ArrayList(); list.add("teste"); • Por exemplo, na segunda linha irá aparecer um warning: “Type safety: The method add(Object) belongs to the raw type List. References to generic type List<E> should be parameterized” • Como tirar o warning?! Genéricos e Herança • Uma classe genérica pode ser derivada de uma classe não-genérica; Por exemplo, a classe Object é uma superclasse direta ou indireta de cada classe genérica; • Uma classe genérica pode ser derivada de outra classe genérica; Por exemplo, a classe Stack (no pacote java.util) é uma subclasse da classe genérica Vector (no pacote java.util); Genéricos e Herança • Uma classe não-genérica pode ser derivada de uma classe genérica; Por exemplo, a classe Properties (no pacote java.util) é uma subclasse da classe genérica Hashtable (no pacote java.util); • Um método genérico em uma subclasse pode sobrescrever um método genérico em uma superclasse se os dois métodos possuírem a mesma assinatura.