Computação Orientada a Objetos - USP · Todas as classes em Java herdam, direta ou indiretamente,...
Transcript of Computação Orientada a Objetos - USP · Todas as classes em Java herdam, direta ou indiretamente,...
Genéricos
Profa. Thienne Johnson
EACH/USP
Java, como programar, 6ª edição◦ Deitel & Deitel
◦ Capítulo 18
Material complementar◦ http://wps.prenhall.com/br_deitel_comoprogra_6/
Todas as classes em Java herdam, direta ouindiretamente, da classe Object (pacotejava.lang)
Classes que implementam estruturas de dados desenvolvidas em Java manipulam e compartilhamobjetos da classe Object
Essas classes não podem manipular variáveis de tipos primitivos, mas podem manipular objetos de classes empacotadoras de tipos
Todo tipo primitivo tem uma classeempacotadora de tipo correspondente (no
pacote java.lang)◦ Boolean
◦ Byte
◦ Character
◦ Double
◦ Float
◦ Integer
◦ Long
◦ Short
Toda classe empacotadora de tipo permitemanipular valores de tipo primitivo comoobjetos
Obs: classes empacotadoras de tipo sãofinal, então não é possível estendê-las
Métodos relacionados a um tipo primitivoencontram-se na classe empacotadora de tipo correspondente
Ex: O método parseInt, que converte umaString em um valor int, encontra-se naclasse Integer
Uma conversão boxing converte um valor de um tipo primitivo em um objeto da classe empacotadora de tipo correspondente
Uma conversão unboxing converte um objeto de uma classe empacotadora de tipo em um valor do tipo primitivo correspondente
J2SE 5.0 permite que essas conversões sejam feitas automaticamente (chamadas de autoboxing e auto-
unboxing)
//cria integerArray
Integer[] integerArray = new Integer[5];
//autoboxing
integerArray[0] = 10; // atribui Integer 10 a
integerArray[0]
//auto-unboxing
int value = integerArray[0]; // obtem valor int de
Integer
Questão 1:
O que fazer para escrever um únicométodo de ordenação ordena paraelementos em um array de Integer, emum array de String ou em um array de qualquer tipo que suporte ordenação?
Questão 2:
O que fazer para escrever uma únicaclasse Stack que seria utilizada comouma pilha de inteiros, uma pilha de números de ponto flutuante, uma pilha de String ou uma pilha de qualquer outrotipo?
Questão 3:
O que fazer para detectar não-
correspondências de tipos em tempo de compilação (segurança de tipos em tempo de compilação)?
Ex: se uma pilha armazenasse somente inteiros, tentar
inserir uma String nessa pilha.
Genéricos: recurso que fornece um meio de criar os objetos gerais citados nas Questões1, 2 e 3
Classes genéricas: permite que o programador defina, com uma únicadeclaração de classe, um conjunto de tiposrelacionados
Métodos genéricos: permite que o programador defina, com uma únicadeclaração de método, um conjunto de métodos relacionados
Os genéricos também fornecem segurançade tipo em tempo de compilação, permitindo a detecção de tipos inválidosem tempo de compilação
1) Escrever um método genérico paraordenar um objeto array e então invocaresse mesmo método com arrays de Integer, arrays de Double, arrays de String para ordenar os elementos no array.
2) Permitir ao compilador realizar umaverificação de tipo para assegurar que o array passado para o método de ordenaçãocontenha elementos do mesmo tipo.
3) Escrever uma única classe Stack genérica quemanipulasse uma pilha de objetos e instanciasseobjetos Stack em uma pilha de Integer, umapilha de Double, uma pilha de String, etc.
Obs: O compilador realizaria a verificação de tipopara assegurar que a estrutura Stack armazenaelementos do mesmo tipo.
Métodos sobrecarregados são bastante utilizadospara realizar operações semelhantes em tiposdiferentes de dados
Ex: Utilização de três métodos printArray
sobrecarregados para imprimir um array de tiposdiferentes.
Método OverloadedMethods.printArray paraimprimir um array de Integer
public static void printArray( Integer[] inputArray )
{
// exibe elementos do array
for ( Integer element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Método OverloadedMethods.printArray paraimprimir um array de Double
public static void printArray( Double[] inputArray )
{
// exibe elementos do array
for ( Double element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Método OverloadedMethods.printArray paraimprimir um array de Character
public static void printArray( Character[] inputArray )
{
// exibe elementos do array
for ( Character element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Quando o compilador encontra uma chamada de método, ele sempre tenta localizar uma declaraçãode método com o mesmo nome de método e parâmetros que correspondam aos tipos de argumento da chamada
No exemplo, cada chamada a printArraycorresponde exatamente a uma das declaraçõesdesse método
...
printArray( integerArray );
...
O compilador determina o tipo do argumentointegerArray (ie, Integer[]) e tenta localizarum método chamado printArray que especificaum único parâmetro Integer[]
Método OverloadedMethods.main
public static void main( String args[] )
{
// cria arrays de Integer, Double e Character
Integer[] integerArray = { 1, 2, 3, 4, 5, 6 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
Character[] characterArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "Array integerArray contem:" );
printArray( integerArray ); // passa um array de Integers
System.out.println( "\nArray doubleArray contem:" );
printArray( doubleArray ); // passa um array Doubles
System.out.println( "\nArray characterArray contem:" );
printArray( characterArray ); // passa um array de Characters
} // fim de main
Saída do programa:
Array integerArray contem:
1 2 3 4 5 6
Array doubleArray contem:
1.1 2.2 3.3 4.4 5.5 6.6 7.7
Array characterArray contem:
H E L L O
Nesse exemplo, os tipos de elementos dos arrays aparecem em:
◦ Nos cabeçalhos dos métodos
public static void printArray( Integer[] inputArray )
public static void printArray( Double[] inputArray )
public static void printArray( Character[] inputArray)
◦ Nas instruções for
for ( Integer element : inputArray )
for ( Double element : inputArray )
for ( Character element : inputArray )
Se os tipos dos elementos em cada métodofossem substituídos por um nome de tipogenérico E, então os três métodos de impressão seriam iguais a:
public static <E> void printArray( E[] inputArray){
// exibe elementos do array
for ( E element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Utilizando um tipo genérico, é possível declararum método printArray que pode exibir as representações string dos elementos de qualquer array que contém objetos
public static <E> void printArray( E[] inputArray ){
// exibe elementos do array
for ( E element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Ex: métodos de impressão para arrays de tipos diferentes
O especificador de formato %s pode ser utilizado para gerar saída de qualquer objetode representação de string – métodotoString é chamado implicitamente
public static <E> void printArray( E[] inputArray ){
// exibe elementos do array
for ( E element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Ex: métodos de impressão para arrays de tipos diferentes
Se as operações realizadas por métodossobrecarregados forem idênticas para cada tipo de argumento, esses métodos podem ser codificadospor métodos genéricos.
◦ Representação mais compacta e conveniente
Pode-se escrever uma única declaração de métodogenérico que pode ser chamada com argumentosde tipos diferentes.
Tradução em tempo de compilação:◦ Com base nos tipos dos argumentos
passados para o método genérico, o compilador trata cada chamada do método de forma apropriada.
Implementando o método printArray
genérico, as chamadas a esse método e as saídas do programa permanecem as mesmas.◦ demonstra o poder expressivo dos genéricos
Todas as declarações de métodos genéricos têmuma seção de parâmetros de tipos, delimitada porcolchetes angulares (ex, <E>) que precedem o tipode retorno do método
Cada seção de parâmetros de tipos contém um oumais parâmetros de tipos, separados por vírgulas
Um parâmetro de tipo (também conhecido comovariável de tipo) é um identificador que especificaum nome genérico do tipo
Os parâmetros de tipo na declaração de um método genérico podem ser utilizados paraespecificar:
◦ o tipo de retorno
◦ tipos de parâmetros
◦ tipos de variáveis locais
Parâmetros de tipo atuam também comomarcadores de lugar para os tipos dos argumentospassados ao método genérico, conhecidos comoargumentos de tipos reais
O corpo de um método genérico é declarado comoo de qualquer outro método
Os parâmetros de tipo podem representar somentetipos por referência – não tipos primitivos, comoint, double e char
Os nomes dos parâmetros de tipo por toda a declaração do método devem corresponder àquelesdeclarados na seção de parâmetro de tipo
Declaração de métodos genéricos
Na instrução for, element é declarado comotipo E, que corresponde ao parâmetro de tipo (E), declarado no cabeçalho do método
public static <E> void printArray( E[] inputArray ){
// exibe elementos do array
for ( E element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Um parâmetro de tipo pode ser declarado somenteuma vez na seção de parâmetro de tipo, mas podeaparecer mais de uma vez na lista de parâmetrosdo método
Ex:
public static <E> void printTwoArrays(E[] array1, E[] array2)
Os nomes de parâmetros de tipo não precisam ser únicos entre diferentes métodos genéricos
Declaração de métodos genéricos
A seção de parâmetro de tipo do métodoprintArray, declara o parâmetro de tipo E
como o marcador de lugar para o tipo de elemento do array que o método enviará para a saída
public static <E> void printArray( E[] inputArray ){
// exibe elementos do array
for ( E element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Declaração de métodos genéricos -Exemplo
A instrução for também utiliza E como o tipo de elemento
public static <E> void printArray( E[] inputArray ){
// exibe elementos do array
for ( E element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Declaração de métodos genéricos -Exemplo
É recomendável que parâmetros de tipo sejamespecificados como letras maiúsculas individuais
Em geral, um parâmetro de tipo que representa o tipo de um elemento em um array (ou em outra
estrutura de dados) é nomeado E, que representa“elemento”
Declaração de métodos genéricos
Quando o compilador encontra a chamada printArray(
integerArray), ele primeiro determina o tipo do argumento integerArray (ie, Integer[]) e tentaencontrar um método printArray com um únicoparâmetro desse tipo. Não há tal método nesse exemplo!
public static void main( String args[] )
{
Integer[] integerArray = { 1, 2, 3, 4, 5, 6 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
Character[] characterArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "Array integerArray contem:" );
printArray( integerArray ); // passa um array de Integers
} // fim de main
Em seguida, o compilador verifica que há um métodogenérico printArray que especifica um parâmetro de array individual, e utiliza o parâmetro de tipo pararepresentar o tipo de elemento do array
public static void main( String args[] )
{
Integer[] integerArray = { 1, 2, 3, 4, 5, 6 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7
};
Character[] characterArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "Array integerArray contem:" );
printArray( integerArray ); // passa um array de Integers
} // fim de main
Métodos genéricos- Tradução em tempo
de compilação
O compilador também determina se as operaçõesno corpo do método genérico podem ser aplicadas a elementos do tipo armazenado no argumento do array
Quando o compilador traduz o método genéricoem bytecode Java, ele remove a seção de parâmetros de tipo e substitui os parâmetros de tipo por tipos reais
Esse processo é chamado de erasure
Métodos genéricos- Tradução em tempo
de compilação
Por padrão, todos os tipos genéricos sãosubstituídos pelo tipo Object
public static void printArray( Object[] inputArray )
{
// exibe elementos do array
for ( Object element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // fim do método printArray
Métodos genéricos- Tradução em tempo
de compilação
O conceito de uma estrutura de dados, comouma pilha, pode ser entendidoindependentemente do tipo que ela manipula
Classes genéricas fornecem um meio de descrever o conceito de uma pilha (ou de qualquer outra classe) de uma maneiraindependente do tipo
É possível, então, instanciar objetos específicosde uma classe genérica
Em tempo de compilação, o compilador Java◦ garante a segurança de tipo do seu código
◦ utiliza técnicas de erasure para permitir que o código do seu cliente interaja com a classegenérica
Uma classe Stack genérica poderia ser a base para criar várias classes Stack, por exemplo:◦ Stack de Double
◦ Stack de Integer
◦ Stack de Character
Essas classes são conhecidas como classes parametrizadas ou tipos parametrizadosporque aceitam um ou mais parâmetros
Lembre-se que parâmetros de tipo sórepresentam tipos por referência◦ a classe genérica Stack não pode instanciada com
tipos primitivos
Entretanto, é possível instanciar uma Stack
que armazena objetos das classes empacotadoras Java e permitir que Java utilize o autoboxing para converter os valoresprimitivos em objetos
A declaração de uma classe genérica se parece com a declaração de uma não-genérica, exceto que o nome da classe é seguido por uma seção de parâmetros de tipo
public class Stack< E >
{
private final int size; // número de elementos na pilha
private int top; // localização do elemento superior
private E[] elements; // array que armazena elementos
na pilha
//...
O parâmetro de tipo E representa o tipo de elemento que a Stack manipulará
O parâmetro de tipo E é utilizado por todadeclaração da classe para representar o tipo do elemento
public class Stack< E >
{
private final int size; // número de elementos na pilha
private int top; // localização do elemento superior
private E[] elements; // array que armazena elementos
na pilha
//...
A classe Stack declara a variável elements comoum array do tipo E
Esse array armazenará os elementos da Stack
Como criar esse array?
public class Stack< E >
{
private final int size; // número de elementos na pilha
private int top; // localização do elemento superior
private E[] elements; // array que armazena elementos
na pilha
//...
Declaração de classes genéricas
Não é permito usar parâmetros de tipo emexpressões de criação de arrays porque esteparâmetro (no caso, E) não estará disponível emtempo de execução
Solução: criar um array do tipo Object e fazer umacoerção na referência retornada por new para o tipoE[]
elements = ( E[] ) new Object[ size ]; // cria o array
Declaração de classes genéricas
Método push.Stack
// insere o elemento na pilha; se bem-sucedido retorna true;
// caso contrário, lança uma FullStackException
public void push( E pushValue )
{
if ( top == size - 1 ) // se a pilha estiver cheia
throw new FullStackException( String.format(
"Stack is full, cannot push %s", pushValue ) );
elements[ ++top ] = pushValue; // insere pushValue na
Stack
} // fim do método push
Declaração de classes genéricas -Exemplo
Método pop.Stack
// retorna o elemento superior se não estiver vazia;
do contrário lança uma EmptyStackException
public E pop()
{
if ( top == -1 ) // se pilha estiver vazia
throw new EmptyStackException( "Stack is
empty, cannot pop" );
return elements[ top-- ]; // remove e retorna o
elemento superior da Stack
} // fim do método pop
Declaração de classes genéricas -Exemplo