Os parâmetros de tipo em genéricos podem ser substituídos por qualquer classe.
Em alguns casos, é útil limitar os tipos que podem ser usados como parâmetro de tipo.
Um exemplo: criar uma classe genérica para armazenar valores numéricos e executar funções matemáticas como calcular o recíproco ou o componente fracionário.
A classe genérica deve permitir operar com diferentes tipos de números, como Integer, Float e Double.
O tipo dos números é especificado genericamente por meio de um parâmetro de tipo.
// NumericFns tenta (sem sucesso) criar uma
// classe genérica que possa executar várias
// funções numéricas, como calcular o recíproco ou o
// componente fracionário, dado qualquer tipo de número.
class NumericFns<T> {
T num;
// Passa para o construtor uma referência
// a um objeto numérico.
NumericFns(T n) {
num = n;
}
// Retorna o recíproco.
double reciprocal() {
return 1 / num.doubleValue(); // Erro!
}
// Retorna o componente fracionário.
double fraction() {
return num.doubleValue() - num.intValue(); // Erro!
}
// ...
}
A classe
NumericFns
não compila porque o compilador não reconhece métodos comodoubleValue()
eintValue()
para o tipo genéricoT
.Isso ocorre porque o compilador não sabe que apenas tipos numéricos deveriam ser usados como argumento para
T
.Para resolver, é necessário limitar o tipo genérico
T
a classes derivadas de uma superclasse específica.Em Java, isso é feito com a cláusula
extends
, como em<T extends superclasse>
.No caso de
NumericFns
, o problema é resolvido definindoNumber
como limite superior para T.
// Nesta versão de NumericFns, o argumento de
// tipo de T deve ser Number ou uma classe
// derivada de Number.
class NumericFns<T extends Number> {
T num;
// Passa para o construtor uma referência
// a um objeto numérico.
NumericFns(T n) {
num = n;
}
// Retorna o recíproco.
double reciprocal() {
return 1 / num.doubleValue();
}
// Retorna o componente fracionário.
double fraction() {
return num.doubleValue() - num.intValue();
}
// ...
}
// Demonstra NumericFns.
class BoundsDemo {
public static void main(String args[]) {
// Integer pode ser usado porque é subclasse de Number.
NumericFns<Integer> iOb =
new NumericFns<Integer>(5);
System.out.println("Reciprocal of iOb is " +
iOb.reciprocal());
System.out.println("Fractional component of iOb is " +
iOb.fraction());
System.out.println();
// Double também pode ser usado.
NumericFns<Double> dOb =
new NumericFns<Double>(5.25);
System.out.println("Reciprocal of dOb is " +
dOb.reciprocal());
System.out.println("Fractional component of dOb is " +
dOb.fraction());
// Essa parte não será compilada porque String
// não é subclasse de Number.
// NumericFns<String> strOb = new NumericFns<String>("Error");
}
}
Limitando o tipo genérico T com Number, o compilador garante que métodos como doubleValue() estejam disponíveis para T.
Essa restrição também impede a criação de objetos NumericFns com tipos não numéricos, como String.
Tipos limitados são úteis para garantir a compatibilidade entre parâmetros de tipo, como demonstrado na classe Pair, que armazena dois objetos compatíveis.
//Aqui, V deve ser do mesmo tipo de T ou uma subclasse de T.
class Pair<T, V extends T> {
T first;
V second;
Pair(T a, V b) {
first = a;
second = b;
}
// ...
}
A classe
Pair
usa dois parâmetros de tipo,T
eV
, ondeV
estendeT
.Isso garante que os dois argumentos passados ao construtor de
Pair
sejam do mesmo tipo ou de tipos relacionados.Exemplo:
T
pode ser uma superclasse, enquantoV
é a mesma classe ou uma subclasse deT
.
// Isto está certo porque T e V são Integer.
Pair<Integer, Integer> x = new Pair<Integer, Integer>(1, 2);
// Isto está certo porque Integer é uma subclasse de Number.
Pair<Number, Integer> y = new Pair<Number, Integer>(10.4, 12);
- Não é possível ter:
// Esta linha causa um erro, porque String não é
// subclasse de Number
Pair<Number, String> z = new Pair<Number, String>(10.4, "12");
-
String
não é subclasse deNumber
, o que viola o limite especificado porPair
.
Top comments (0)