Baixe o app para aproveitar ainda mais
Prévia do material em texto
Classes com tipos genéricos APRESENTAÇÃO Tipos genéricos são muito usados nas APIs da linguagem java. Como exemplo, podemos citar as coleções, as quais também podem ser implementadas em nossas aplicações. Nesta Unidade de Aprendizagem veremos alguns conceitos sobre classes com tipos genéricos. Trabalharemos um escopo básico, já que este assunto é muito extenso, mas é necessário que todos os programadores tenham um conhecimento básico. Bons estudos. Ao final desta Unidade de Aprendizagem, você deve apresentar os seguintes aprendizados: Definir o que é um tipo genérico.• Reconhecer classes genéricas.• Construir uma aplicação utilizando tipos genéricos.• DESAFIO Classes genéricas podem ter mais de um parâmetro de tipo, isto é, mais uma das facilidades que os tipos genéricos nos proporcionam. Você é analista/programador em uma fábrica de software e seu trabalho é entregar uma aplicação de cadastro de clientes. Para isso, deve criar uma classe modelo e uma de controle para instanciar a modelo, no entanto, deve utilizar as técnicas de tipos genéricos. O cadastro de cliente deverá conter nome, sobrenome, idade, endereço e e-mail. Para executar o desafio, utilize uma linguagem de programação orientada a objetos, um IDE para auxiliar na organização do projeto (sugerido: netBeans ou Eclipse). Crie um projeto com o nome de cadastro; dentro, crie um pacote com o mesmo nome, e, dentro do pacote, crie as classes necessárias. Observação: a classe controle, além de instanciar a classe modelo e inserir os valores, deverá invocar um método para impressão do objeto na tela. INFOGRÁFICO Veja no infográfico os conceitos de classes com tipos genéricos. CONTEÚDO DO LIVRO Os genéricos adicionaram um elemento de sintaxe totalmente novo e causaram mudanças em muitas das classes e métodos da API principal. Não é exagero dizer que sua inclusão basicamente reformulou a natureza de Java. Acompanhe um trecho do livro Java para iniciantes, o qual traz uma abordagem sobre tipos genéricos e é a base teórica para esta Unidade de Aprendizagem. Inicie o estudo pelo tópico Fundamentos dos tipos genéricos e finalize ao final de Tipos genéricos diferem de acordo com seus argumentos de tipo. Boa leitura. Atualizado para JAVA SE 8 (JDK 8) Java para iniciantes Crie, compile e execute programas Java rapidamente Herbert Schildt Catalogação na publicação: Poliana Sanchez de Araujo – CRB 10/2094 S334j Schildt, Herbert. Java para iniciantes : crie, compile e execute programas Java rapidamente [recurso eletrônico] / Herbert Schildt ; tradução: Aldir José Coelho Corrêa da Silva ; revisão técnica: Maria Lúcia Blanck Lisbôa. – 6. ed. – Porto Alegre : Bookman, 2015. Editado como livro impresso em 2015. ISBN 978-85-8260-337-6 1. Linguagem de programação - Java. I. Título. CDU 004.438Java O autor O autor de best-sellers Herbert Schildt escreve incansavelmente sobre programação há quase três décadas e é uma das principais autoridades na linguagem Java. Seus livros de programação venderam milhões de cópias no mundo inteiro e foram tra- duzidos para diversos idiomas. É autor de vários livros sobre Java, incluindo Java: The Complete Reference, Herb Schildt’s Java Programming Cookbook e Swing: A Beginner’s Guide. Ele também escreveu sobre C, C++ e C#. Embora tenha interesse em todas as áreas da computação, seu foco principal são as linguagens de progra- mação, incluindo compiladores, interpretadores e linguagens de controle robótico. Também tem grande interesse na padronização de linguagens. Schildt tem gradua- ção e pós-graduação pela Universidade de Illinois. Seu site é www.HerbSchildt.com. O editor técnico Dr. Danny Coward trabalhou em todas as edições da plataforma Java. Ele conduziu a definição dos Java Servlets para a primeira versão da plataforma Java EE e para além dela, os serviços web para a plataforma Java ME, e a estratégia e planejamento de Java SE 7. Fundou a tecnologia JavaFX e, mais recentemente, projetou o maior acréscimo feito ao padrão Java EE 7, a API Java WebSocket. Da codificação em Java ao projeto de APIs com especialistas da indústria e ao trabalho por vários anos como executivo do Java Community Process, ele adquiriu uma perspectiva singularmente ampla de vários aspectos da tecnologia Java. Além disso, é autor de JavaWebSo- cket Programming e de um livro ainda a ser publicado sobre Java EE. Dr. Coward tem graduação, mestrado e doutorado em Matemática pela Universidade de Oxford. Capítulo 13 Tipos genéricos 429 Principais habilidades e conceitos • Entender as vantagens dos tipos genéricos • Criar uma classe genérica • Aplicar parâmetros de tipo limitado • Usar argumentos curingas • Aplicar curingas limitados • Criar um método genérico • Criar um construtor genérico • Criar uma interface genérica • Utilizar tipos brutos • Aplicar a inferência de tipos com o operador losango • Entender a técnica erasure • Evitar erros de ambiguidade • Conhecer as restrições dos genéricos ..................................................................................................................................... Desde sua versão original, muitos recursos novos foram adicionados a Java. Todos melhoraram e expandiram seu escopo, mas o que teve impacto parti- cularmente profundo e extenso foi o tipo genérico, porque seus efeitos foram sen- tidos em toda a linguagem. Por exemplo, os genéricos adicionaram um elemento de sintaxe totalmente novo e causaram mudanças em muitas das classes e métodos da API principal. Não é exagero dizer que sua inclusão basicamente reformulou a natureza de Java. O tópico “genéricos” é muito extenso e parte dele é avançado demais para entrar no escopo deste livro. No entanto, um conhecimento básico dos genéricos é necessário a todos os programadores Java. À primeira vista, a sintaxe dos genéricos pode parecer um pouco complicada, mas não se preocupe, os genéricos são muito fáceis de usar. Quando você terminar este capítulo, terá uma noção dos conceitos- -chave que estão por trás dos genéricos e terá conhecimento suficiente para usá-los de maneira eficaz em seus próprios programas. Fundamentos dos tipos genéricos Na verdade, com o termo genéricos queremos nos referir aos tipos parametrizados. Os tipos parametrizados são importantes porque nos permitem criar classes, inter- 430 Java para Iniciantes faces e métodos em que o tipo de dado usado é especificado como parâmetro. Uma classe, interface ou método que opera sobre um parâmetro de tipo é chamado de genérico, como em classe genérica ou método genérico. Uma vantagem importante do código genérico é que ele funciona automatica- mente com o tipo de dado passado para seu parâmetro de tipo. Muitos algoritmos são logicamente iguais, não importando o tipo de dado ao qual estão sendo aplicados. Por exemplo, uma classificação rápida é igual classificando itens de tipo Integer, String, Object ou Thread. Com os genéricos, você pode definir um algoritmo uma única vez, independentemente do tipo de dado, e então aplicá-lo a uma ampla varie- dade de tipos de dados sem nenhum esforço adicional. É importante entender que Java sempre permitiu a criação de classes, interfaces e métodos generalizados usando referências de tipo Object. Já que Object é a super- classe de todas as outras classes, uma referência Object pode referenciar qualquer tipo de objeto. Logo, em códigos anteriores aos genéricos, classes, interfaces e méto- dos generalizados usavam referências Object para operar com vários tipos de dados. O problema é que eles não faziam isso com segurança de tipos, já que coerções eram necessárias para converter explicitamente Object no tipo de dado que estava sendo tratado. Portanto, era possível gerar acidentalmente discrepâncias de tipo. Os genéricos adicionam a segurança de tipos que estava faltando, porque tornam essas coerções automáticas e implícitas. Resumindo, eles expandem nossa habilidade de reutilizar código e nos permitemfazê-lo de maneira segura e confiável. Exemplo simples de genérico Antes de discutir mais teoria, é melhor examinarmos um exemplo simples de genéri- co. O programa a seguir define duas classes. A primeira é a classe genérica Gen e a segunda é GetDemo, que usa Gen. // Classe genérica simples. // Aqui, T é um parâmetro de tipo que // será substituído pelo tipo real quando // um objeto de tipo Gen for criado. class Gen<T> { T ob; // declara um objeto de tipo T Declara uma classe genérica. T é o parâmetro de tipo genérico. P: Ouvi dizer que os genéricos Java são semelhantes aos templates de C++. É isso mesmo? R: Sim, os genéricos Java são semelhantes aos templates de C++. O que Java chama de tipo parametrizado, C++ chama de template. No entanto, os genéricos Java e os tem- plates C++ não são iguais e há algumas diferenças básicas entre as duas abordagens de tipos genéricos. Geralmente, a abordagem Java é mais fácil de usar. Uma advertência: se você tiver experiência em C++, é importante não tirar con- clusões precipitadas sobre como os genéricos funcionam em Java. As duas abordagens de código genérico diferem de maneiras sutis, mas básicas. Pergunte ao especialista Capítulo 13 Tipos genéricos 431 // Passa para o construtor uma // referência a um objeto de tipo T Gen(T o) { ob = o; } // Retorna ob. T getob() { return ob; } // Exibe o tipo de T. void showType() { System.out.println("Type of T is " + ob.getClass().getName()); } } // Demonstra a classe genérica. class GenDemo { public static void main(String args[]) { // Cria uma referência Gen para Integers. Gen<Integer> iOb; // Cria um objeto Gen<Integer> e atribui sua // referência a iOb. Observe o uso do autoboxing no // encapsulamento do valor 88 dentro de um objeto Integer. iOb = new Gen<Integer>(88); // Exibe o tipo de dado usado por iOb. iOb.showType(); // Obtém o valor de iOb. Observe // que nenhuma coerção é necessária. int v = iOb.getob(); System.out.println("value: " + v); System.out.println(); // Cria um objeto Gen para Strings. Gen<String> strOb = new Gen<String>("Generics Test"); // Exibe o tipo de dado usado por strOb. strOb.showType(); // Obtém o valor de strOb. Novamente, observe // que nenhuma coerção é necessária. String str = strOb.getob(); System.out.println("value: " + str); } } Cria uma referência a um objeto de tipo Gen<Integer>. Instancia um objeto de tipo Gen<Integer>. Cria uma referência e um objeto de tipo Gen<String>. 432 Java para Iniciantes A saída produzida pelo programa é mostrada abaixo: Type of T is java.lang.Integer value: 88 Type of T is java.lang.String value: Generics Test Examinemos esse programa com detalhes. Primeiro, observe como Gen é de- clarada pela linha a seguir: class Gen<T> { Aqui, T é o nome de um parâmetro de tipo. Esse nome é usado como espaço reser- vado para o tipo real que será passado para Gen quando um objeto for criado. Logo, T será usado dentro de Gen sempre que o parâmetro de tipo for necessário. Observe que T está dentro de < >. Essa sintaxe pode ser generalizada. Sempre que um pa- râmetro de tipo estiver sendo declarado, ele será especificado dentro de colchetes angulares (< >). Já que Gen usa um parâmetro de tipo, é uma classe genérica. Na declaração de Gen, não há um significado especial no nome T. Qualquer identificador válido poderia ter sido usado, mas o uso de T é tradicional. Além disso, é recomendável que os nomes dos parâmetros de tipo tenham apenas um caractere: uma letra maiúscula. Outros nomes de parâmetros de tipo normalmente usados são V e E. Em seguida, T é usado para declarar um objeto chamado ob, como mostrado abaixo: T ob; // declara um objeto de tipo T Como explicado, T é um espaço reservado para o tipo real que será especificado quando um objeto Gen for criado. Logo, ob será um objeto do tipo passado para T. Por exemplo, se o tipo String for passado para T, então, nesse caso, ob será de tipo String. Agora, considere o construtor de Gen: Gen(T o) { ob = o; } Observe que seu parâmetro, o, é de tipo T. Ou seja, o tipo real de o será determinado pelo tipo passado para T quando um objeto Gen for criado. Além disso, já que tanto o parâmetro o quanto a variável membro ob são de tipo T, ambos terão o mesmo tipo quando da criação de um objeto Gen. O parâmetro de tipo T também pode ser usado para especificar o tipo de retor- no de um método, como ocorre com o método getob( ), mostrado aqui: T getob() { return ob; } Já que ob também é de tipo T, seu tipo é compatível com o tipo de retorno especifi- cado por getob( ). Capítulo 13 Tipos genéricos 433 O método showType( ) exibe o tipo de T. Ele faz isso chamando getName( ) no objeto Clas retornado pela chamada a getClass( ) em ob. Não usamos esse recur- so antes, logo, vamos examiná-lo em detalhes. Você deve lembrar que, no Capítulo 7, vimos que a classe Object define o método getClass( ). Portanto, getClass( ) é membro de todos os tipos de classe. Ele retorna um objeto Class correspondente ao tipo de classe do objeto em que foi chamado. Class é uma classe definida dentro de java.lang que encapsula informações sobre outra classe. Ela define vários métodos que podem ser usados na obtenção de informações sobre uma classe no tempo de execução. Entre eles, está o método getName( ), que retorna uma representação do nome da classe na forma de string. A classe GenDemo demonstra a classe genérica Gen. Primeiro, ela cria uma versão de Gen para inteiros, como vemos abaixo: Gen<Integer> iOb; Examine bem essa declaração. Primeiro, observe que o tipo Integer é especificado dentro de colchetes angulares após Gen. Nesse caso, Integer é um argumento de tipo que é passado para o parâmetro de tipo de Gen, que é T. Isso cria uma versão de Gen em que todas as referências a T são convertidas para referências a Integer. Logo, para essa declaração, ob é de tipo Integer e o tipo de retorno de getob( ) também. Antes de prosseguirmos, é preciso dizer que o compilador Java não cria real- mente versões diferentes de Gen ou de qualquer outra classe genérica. Embora seja útil pensar assim, não é o que acontece. Em vez disso, o compilador remove todas as informações do tipo genérico, substituindo pelas coerções necessárias, para fazer o có- digo se comportar como se uma versão específica de Gen fosse criada. Logo, na ver- dade, há apenas uma versão de Gen no programa. O processo de remover informações do tipo genérico se chama erasure e ele será discutido posteriormente neste capítulo. A próxima linha atribui a iOb uma referência a uma instância de uma versão Integer da classe Gen. iOb = new Gen<Integer>(88); Observe que quando o construtor de Gen é chamado, o argumento de tipo Integer também é especificado. Isso é necessário porque o objeto (nesse caso, iOb) ao qual a referência está sendo atribuída é de tipo Gen<Integer>. Logo, a referência retornada por new também deve ser de tipo Gen<Integer>. Se não for, ocorrerá um erro de tempo de compilação. Por exemplo, a atribuição a seguir causará um erro de tempo de compilação: iOb = new Gen<Double>(88.0); // Erro! Já que iOb é de tipo Gen<Integer>, não pode ser usada para referenciar um objeto de Gen<Double>. Esse tipo de verificação é um dos principais benefícios dos gené- ricos porque assegura a segurança dos tipos. Como os comentários do programa informam, a atribuição iOb = new Gen<Integer>(88); faz uso do autoboxing para encapsular o valor 88, que é um int, em um Integer. Isso funciona porque Gen<Integer> cria um construtor que recebe um argumento 434 Java para Iniciantes Integer. Já que um Integer é esperado, Java encapsulará automaticamente 88 dentro dele. É claro que a atribuição também poderia ter sido escrita explicitamente, da seguinte forma: iOb = new Gen<Integer>(new Integer(88)); No entanto, não teríamosvantagem usando essa versão. Em seguida, o programa exibe o tipo de ob dentro de iOb, que é Integer. De- pois, obtém o valor de ob usando a linha abaixo: int v = iOb.getob(); Como o tipo de retorno de getob( ) é T, que foi substituído por Integer quando iOb foi declarada, ele também é Integer, que é encapsulado em int quando atribuído a v (que é um int). Logo, não há necessidade de converter o tipo de retorno de getob( ) para Integer. Agora, GenDemo declara um objeto de tipo Gen<String>: Gen<String> strOb = new Gen<String>("Generics Test"); Como o argumento de tipo é String, T é substituído por String dentro de Gen. Isso cria (conceitualmente) uma versão String de Gen, como as linhas restantes do pro- grama demonstram. Genéricos só funcionam com tipos de referência Na declaração de uma instância de um tipo genérico, o argumento de tipo passado para o parâmetro de tipo deve ser um tipo de referência. Você não pode usar um tipo primitivo, como int ou char. Por exemplo, com Gen, é possível passar qualquer tipo de classe para T, mas você não pode passar um tipo primitivo para T. Logo, a decla- ração a seguir é inválida: Gen<int> intOb = new Gen<int>(53); // Erro, não pode usar um tipo primitivo Certamente, não poder especificar um tipo primitivo não é uma restrição grave, por- que você pode usar os encapsuladores de tipos (como fez o exemplo anterior) para encapsular um tipo primitivo. Além disso, o mecanismo Java de autoboxing e autou- nboxing torna o uso do encapsulador de tipos transparente. Tipos genéricos diferem de acordo com seus argumentos de tipo Um ponto-chave que devemos entender sobre os tipos genéricos é que uma referên- cia de uma versão específica de um tipo genérico não tem compatibilidade de tipo com outra versão do mesmo tipo genérico. Por exemplo, supondo o programa que acabei de mostrar, a linha de código abaixo está errada e não será compilada: iOb = strOb; // Errado! Ainda que tanto iOb quanto strOb sejam de tipo Gen<T>, são referências a tipos diferentes porque seus argumentos de tipo diferem. Isso faz parte da maneira como os genéricos adicionam segurança de tipos e evitam erros. DICA DO PROFESSOR Veja no vídeo a seguir os conceitos de classes com tipos genéricos. Conteúdo interativo disponível na plataforma de ensino! EXERCÍCIOS 1) Em relação a tipos genéricos, marque a alternativa INCORRETA. A) O tópico genérico é extenso. B) Genéricos não são tipos primitivos de dados. C) Quando declaramos um atributo como String, estamos declarando como genérico. D) Com um genérico você pode definir um algoritmo apenas uma vez, independentemente do tipo de dados. E) Com genéricos, podemos reutilizar código. 2) Nos fundamentos dos tipos genéricos, quando utilizamos o termo “genérico”, estamos nos referindo a: A) Estrutura de herança. B) Um tipo primitivo de dados que pode receber qualquer valor. C) Quando uma aplicação pode ser executada sem o método main em java. D) Usamos variáveis de referência ao instanciarmos uma classe. E) Quando nos referimos a tipos parametrizados. 3) Para declararmos uma classe que utilizará tipos genéricos, utilizamos qual sintaxe? A) class NomeDaClasse{ //implementação. } B) class abstract NomeDaClasse{ //implementação. } C) class NomeDaClasse extends Generica{ //implementação. } D) class NomeDaClasse implements Generica{ //implementação. } E) class NomeDaClasse <Parâmetro de tipo>{ //implementação. } Analise o código e marque a alternativa INCORRETA. 1 - class Gen<T>{ 2 - T ob; 3 - 4 - Gen(T o) { 5 - 6 - ob = o; 7 - 8 - } 9 - 10 - T getob(){ 11 - return ob; 4) 12 - } 13 - 14 - void showType() { 15 - 16 - System.out.println("Type of T is " + ob.getClass().getName()); 17 - 18 - } 19 - } 20 - 21 - 22 - //Demonstra a classe genérica. 23 - 24 - class GenDemo { 25 - 26 - public static void main(String[] args){ 27 - 28 - Gen<Integer> iOb = new Gen<Integer>(88); 29 - 30 - iOb.showType(); 31 - 32 - int v = iOb.getob(); 33 - 34 - System.out.println("Value: " + v); 35 - 36 - System.out.println(); 37 - 38 - Gen<String> strOb = new Gen<String>("Generics Test"); 39 - 40 - strOb.showType(); 41 - 42 - String str = strOb.getob(); 43 - 44 - System.out.println("Value: "+ str); 45 - 46 - } 47 - } A) Veja o trecho de código escrito na linha 1. class Gen<T>{ A letra T, representa o nome de um parâmetro de tipo. B) Veja o trecho de código escrito na linha 2. T ob; Teremos um erro de compilação, pois não estamos definindo um tipo válido para o atributo ob. C) Veja o trecho de código escrito nas linhas 10,11 e 12. T getob(){ return ob; } Temos o método getob() que retorna um tipo T. D) Veja o trecho de código escrito na linha 28. Gen iOb = new Gen(88); Estamos criando uma versão de Gen para inteiros e atribuindo uma referência a iOb a uma instância da classe GEN. E) Veja o trecho de código escrito na linha 38. Gen strOb = new Gen("Generics Test"); Aqui, GenDemo está declarando um objeto do tipo Gen. 5) Marque a alternativa INCORRETA. A) Genéricos só funcionam com tipos de referência. B) Tipos genéricos diferem de acordo com seus argumentos de tipo. C) Classes genéricas podem ser declaradas com mais de um parâmetro. D) É recomendável que os nomes de parâmetros de tipos tenham apenas uma letra. E) As letras a serem utilizadas como nomes de parâmetros de tipos devem ser apenas “T”, “E” ou “V”. NA PRÁTICA Veja no vídeo uma aplicação prática de classes com tipos genéricos. Conteúdo interativo disponível na plataforma de ensino! SAIBA MAIS Para ampliar o seu conhecimento a respeito desse assunto, veja abaixo as sugestões do professor: Você é realmente um bom desenvolvedor Java? Para ser um bom desenvolvedor Java é preciso, acima de tudo, ter experiência. Nesse artigo, leia mais sobre o assunto. Conteúdo interativo disponível na plataforma de ensino! Artigo Introdução: Sobrecarga de Métodos e Tipos Genéricos em Java eja nesse artigo como e quando utilizar sobrecarga de métodos e tipos genéricos e quais problemas cada um ajuda a resolver. Conteúdo interativo disponível na plataforma de ensino! Conceitos de Computação com Java - Compatível com Java 5 & 6 Cay Horstmann, 2009, 5ª Edição
Compartilhar