Java/Genéricos: diferenças entre revisões
[edição verificada] | [revisão pendente] |
m <source> -> <syntaxhighlight> (phab:T237267) |
|||
Linha 7: | Linha 7: | ||
Na documentação <code>[https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html java.util.ArrayList]</code> há as seguintes assinaturas de método que utilizam o tipo genérico '''E'''. |
Na documentação <code>[https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html java.util.ArrayList]</code> há as seguintes assinaturas de método que utilizam o tipo genérico '''E'''. |
||
< |
<syntaxhighlight lang="Java"> |
||
public boolean add(E e) |
public boolean add(E e) |
||
public E get(int index) |
public E get(int index) |
||
</syntaxhighlight> |
|||
</source> |
|||
É convencionado o identificador '''E''' para elementos, '''K''' para chaves, '''V''' para valores e '''T''' para tipos, porém não há obrigatoriedade no uso. Pode-se utilizar qualquer identificador válido no parâmetro de tipo. |
É convencionado o identificador '''E''' para elementos, '''K''' para chaves, '''V''' para valores e '''T''' para tipos, porém não há obrigatoriedade no uso. Pode-se utilizar qualquer identificador válido no parâmetro de tipo. |
||
Linha 17: | Linha 17: | ||
No código abaixo temos a definição da classe <code>java.util.ArrayList</code> e da interface <code>java.util.List</code>, ambas com o parâmetro de tipo definidos com o tipo genérico '''E'''. |
No código abaixo temos a definição da classe <code>java.util.ArrayList</code> e da interface <code>java.util.List</code>, ambas com o parâmetro de tipo definidos com o tipo genérico '''E'''. |
||
< |
<syntaxhighlight lang="Java"> |
||
//Declarações parciais para evidenciar apenas os conceitos apresentados. |
//Declarações parciais para evidenciar apenas os conceitos apresentados. |
||
class ArrayList<E> {...} |
class ArrayList<E> {...} |
||
interface List<E> {...} |
interface List<E> {...} |
||
</syntaxhighlight> |
|||
</source> |
|||
'''Argumento de tipo''' é o tipo especificado, entre o par de chevrons, na declaração de um tipo (chamada de construtor ou variável de referência) que substituirá o tipo genérico. |
'''Argumento de tipo''' é o tipo especificado, entre o par de chevrons, na declaração de um tipo (chamada de construtor ou variável de referência) que substituirá o tipo genérico. |
||
No código abaixo o tipo genérico '''E''' foi substituído, em tempo de compilação, pelo argumento de tipo '''String'''. Com isso o tipo da variável na lista de parâmetros do método <code>boolean add(E e)</code> foi alterado de <code>E e</code> para <code>String e</code> e o tipo de retorno do método <code>E get(int index)</code> foi de <code>E</code> para <code>String</code>. |
No código abaixo o tipo genérico '''E''' foi substituído, em tempo de compilação, pelo argumento de tipo '''String'''. Com isso o tipo da variável na lista de parâmetros do método <code>boolean add(E e)</code> foi alterado de <code>E e</code> para <code>String e</code> e o tipo de retorno do método <code>E get(int index)</code> foi de <code>E</code> para <code>String</code>. |
||
< |
<syntaxhighlight lang="Java" highlight="1-3"> |
||
List<String> lista = new ArrayList<String>(); |
List<String> lista = new ArrayList<String>(); |
||
lista.add("Elemento"); |
lista.add("Elemento"); |
||
String elemento = lista.get(0); |
String elemento = lista.get(0); |
||
System.out.println(elemento); |
System.out.println(elemento); |
||
</syntaxhighlight> |
|||
</source> |
|||
<pre> |
<pre> |
||
Elemento |
Elemento |
||
Linha 39: | Linha 39: | ||
Para declarar-se classe ou interface com referências de tipos genéricos, basta especificar o tipo a ser substituído no parâmetro de tipo. |
Para declarar-se classe ou interface com referências de tipos genéricos, basta especificar o tipo a ser substituído no parâmetro de tipo. |
||
< |
<syntaxhighlight lang="Java"> |
||
class Objeto<T> { |
class Objeto<T> { |
||
T objeto; |
T objeto; |
||
Linha 51: | Linha 51: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
Ao utilizar-se uma classe ou interface que utiliza tipos genéricos basta enviar no argumento de tipo o tipo desejado. No código abaixo <code>String</code> foi definido como o tipo na variável de referência (<code>Objeto<String> obj</code>) e na chamada do construtor (<code>new Objeto<String>()</code>). |
Ao utilizar-se uma classe ou interface que utiliza tipos genéricos basta enviar no argumento de tipo o tipo desejado. No código abaixo <code>String</code> foi definido como o tipo na variável de referência (<code>Objeto<String> obj</code>) e na chamada do construtor (<code>new Objeto<String>()</code>). |
||
< |
<syntaxhighlight lang="Java" highlight="1"> |
||
Objeto<String> obj = new Objeto<String>(); |
Objeto<String> obj = new Objeto<String>(); |
||
obj.setObjeto("Uma string qualquer."); |
obj.setObjeto("Uma string qualquer."); |
||
System.out.println(obj.getObjeto()); |
System.out.println(obj.getObjeto()); |
||
</syntaxhighlight> |
|||
</source> |
|||
<pre> |
<pre> |
||
Uma string qualquer. |
Uma string qualquer. |
||
Linha 66: | Linha 66: | ||
Pode-se definir mais de um tipo de parâmetro. Para tal, é necessário separá-los com vírgula e não utilizar o mesmo tipo já utilizado. |
Pode-se definir mais de um tipo de parâmetro. Para tal, é necessário separá-los com vírgula e não utilizar o mesmo tipo já utilizado. |
||
< |
<syntaxhighlight lang="Java"> |
||
class Par<K, T> { |
class Par<K, T> { |
||
K chave; |
K chave; |
||
Linha 96: | Linha 96: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
<pre> |
<pre> |
||
42 é a resposta para o universo. |
42 é a resposta para o universo. |
||
Linha 102: | Linha 102: | ||
A partir do Java SE 7 pode-se utilizar o operador diamante, que nada mais é que o argumento de tipo sem tipo especificado na chamada do construtor. Em tempo de compilação o argumento de tipo do construtor será inserido em correspondência ao argumento de tipo do tipo da variável. |
A partir do Java SE 7 pode-se utilizar o operador diamante, que nada mais é que o argumento de tipo sem tipo especificado na chamada do construtor. Em tempo de compilação o argumento de tipo do construtor será inserido em correspondência ao argumento de tipo do tipo da variável. |
||
< |
<syntaxhighlight lang="Java"> |
||
Par<Integer, String> teste = new Par<>(); |
Par<Integer, String> teste = new Par<>(); |
||
</syntaxhighlight> |
|||
</source> |
|||
É possível definir uma classe ou interface com tipos genéricos em parâmetros de tipo. |
É possível definir uma classe ou interface com tipos genéricos em parâmetros de tipo. |
||
< |
<syntaxhighlight lang="Java" highlight="1"> |
||
Par<Integer, Objeto<String>> par = new Par<>(52, new Objeto<>()); |
Par<Integer, Objeto<String>> par = new Par<>(52, new Objeto<>()); |
||
par.getObjeto().setObjeto("objeto"); |
par.getObjeto().setObjeto("objeto"); |
||
System.out.println(par.getChave() + ": " + par.getObjeto().getObjeto() + "\n"); |
System.out.println(par.getChave() + ": " + par.getObjeto().getObjeto() + "\n"); |
||
</syntaxhighlight> |
|||
</source> |
|||
<pre> |
<pre> |
||
52: objeto |
52: objeto |
||
Linha 119: | Linha 119: | ||
É possível repassar o parâmetro de tipo definido em uma classe como argumento de tipo para referências de tipo genérico. No código abaixo a classe '''Colecao''' foi definida com o tipo '''T''' em seu parâmetro de tipo. O tipo '''T''' é então utilizado como argumento de tipo da variável de referência <code>List<T> lista</code>. |
É possível repassar o parâmetro de tipo definido em uma classe como argumento de tipo para referências de tipo genérico. No código abaixo a classe '''Colecao''' foi definida com o tipo '''T''' em seu parâmetro de tipo. O tipo '''T''' é então utilizado como argumento de tipo da variável de referência <code>List<T> lista</code>. |
||
< |
<syntaxhighlight lang="Java" highlight="1-2,4,8"> |
||
class Colecao<T> { |
class Colecao<T> { |
||
List<T> lista; |
List<T> lista; |
||
Linha 139: | Linha 139: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
<pre> |
<pre> |
||
[5.4, 3.43] |
[5.4, 3.43] |
||
Linha 151: | Linha 151: | ||
No código abaixo, '''T''' é restringido à '''CharSequence''' ou suas classes implementadoras. |
No código abaixo, '''T''' é restringido à '''CharSequence''' ou suas classes implementadoras. |
||
< |
<syntaxhighlight lang="Java" line start="6" highlight="1"> |
||
class Teste<T extends CharSequence> { |
class Teste<T extends CharSequence> { |
||
public static void main(String[] args) { |
public static void main(String[] args) { |
||
Linha 164: | Linha 164: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
<pre> |
<pre> |
||
Ex |
Ex |
||
Linha 190: | Linha 190: | ||
Para isso basta inserir o parâmetro de tipo antes do tipo de retorno. |
Para isso basta inserir o parâmetro de tipo antes do tipo de retorno. |
||
< |
<syntaxhighlight lang="Java" highlight="1"> |
||
<T> void metodo () {} |
<T> void metodo () {} |
||
</syntaxhighlight> |
|||
</source> |
|||
No código abaixo o tipo genérico, definido como <code>T</code>, é utilizado no tipo de retorno e no tipo do parâmetro na lista de parâmetros da declaração de método. Em tempo de compilação, o tipo <code>T</code> será convertido no tipo do argumento do método chamador, no caso abaixo como o argumento do método é do tipo <code>String</code> ('''"Exemplo"''') logo o tipo definido será <code>String</code>. |
No código abaixo o tipo genérico, definido como <code>T</code>, é utilizado no tipo de retorno e no tipo do parâmetro na lista de parâmetros da declaração de método. Em tempo de compilação, o tipo <code>T</code> será convertido no tipo do argumento do método chamador, no caso abaixo como o argumento do método é do tipo <code>String</code> ('''"Exemplo"''') logo o tipo definido será <code>String</code>. |
||
< |
<syntaxhighlight lang="Java" highlight="7"> |
||
class Teste { |
class Teste { |
||
public static void main(String[] args) { |
public static void main(String[] args) { |
||
Linha 207: | Linha 207: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
<pre> |
<pre> |
||
Exemplo |
Exemplo |
||
Linha 215: | Linha 215: | ||
Para manter-se a compatibilidade com versões anteriores ao Java SE 5, a partir do lançamento da referida versão foi permitido que argumentos de tipo possam ser omitidos, são os chamados '''Tipos Crus'''. |
Para manter-se a compatibilidade com versões anteriores ao Java SE 5, a partir do lançamento da referida versão foi permitido que argumentos de tipo possam ser omitidos, são os chamados '''Tipos Crus'''. |
||
< |
<syntaxhighlight lang="Java"> |
||
//Tipo parametrizado. |
//Tipo parametrizado. |
||
Set<Double> conjunto; |
Set<Double> conjunto; |
||
Linha 221: | Linha 221: | ||
//Tipo cru. |
//Tipo cru. |
||
Set conjunto; |
Set conjunto; |
||
</syntaxhighlight> |
|||
</source> |
|||
Em tempo de compilação os tipos crus são convertidos para o tipo <code>Object</code>. |
Em tempo de compilação os tipos crus são convertidos para o tipo <code>Object</code>. |
||
< |
<syntaxhighlight lang="Java" highlight="1, 3" start="7" line> |
||
List lista = new ArrayList(); |
List lista = new ArrayList(); |
||
lista.add(5.5); |
lista.add(5.5); |
||
int i = (int) lista.get(0); |
int i = (int) lista.get(0); |
||
System.out.println(i); |
System.out.println(i); |
||
</syntaxhighlight> |
|||
</source> |
|||
5.5 |
5.5 |
||
Edição atual desde as 15h42min de 16 de abril de 2020
Genéricos, também conhecidos como Tipos Genéricos, são parâmetros de tipos em classes, métodos e interfaces. É possível definir tipos genéricos para as variáveis de referência dentro de um determinado escopo (classes, interfaces e métodos) e ao realizar uma chamada ao método, ou definir um variável de referência e/ou instanciar um construtor os tipos genéricos são substituídos pelo tipo especificado.
Com a utilização de genéricos é possível abstrair o tipo de parâmetro e/ou de retorno de métodos, o que garante maior reaproveitamento de código.
Parâmetro de tipo[editar | editar código-fonte]
Como visto no capítulo sobre Collections é possível definir o tipo do elemento que será armazenado e recuperado de uma coleção ao especificar-se na variável entre um par de chevrons, <
e >
, o tipo desejado. Esse tipo utilizado nas variáveis da lista de parâmetros e no tipo de retorno dos métodos é chamado de tipo genérico.
Na documentação java.util.ArrayList
há as seguintes assinaturas de método que utilizam o tipo genérico E.
public boolean add(E e)
public E get(int index)
É convencionado o identificador E para elementos, K para chaves, V para valores e T para tipos, porém não há obrigatoriedade no uso. Pode-se utilizar qualquer identificador válido no parâmetro de tipo.
Parâmetro de tipo é o tipo genérico definido entre o par de chevrons na definição de uma classe ou interface. Deve ser inserido após o identificador da classe.
No código abaixo temos a definição da classe java.util.ArrayList
e da interface java.util.List
, ambas com o parâmetro de tipo definidos com o tipo genérico E.
//Declarações parciais para evidenciar apenas os conceitos apresentados.
class ArrayList<E> {...}
interface List<E> {...}
Argumento de tipo é o tipo especificado, entre o par de chevrons, na declaração de um tipo (chamada de construtor ou variável de referência) que substituirá o tipo genérico.
No código abaixo o tipo genérico E foi substituído, em tempo de compilação, pelo argumento de tipo String. Com isso o tipo da variável na lista de parâmetros do método boolean add(E e)
foi alterado de E e
para String e
e o tipo de retorno do método E get(int index)
foi de E
para String
.
List<String> lista = new ArrayList<String>();
lista.add("Elemento");
String elemento = lista.get(0);
System.out.println(elemento);
Elemento
Para declarar-se classe ou interface com referências de tipos genéricos, basta especificar o tipo a ser substituído no parâmetro de tipo.
class Objeto<T> {
T objeto;
T getObjeto() {
return objeto;
}
void setObjeto(T objeto) {
this.objeto = objeto;
}
}
Ao utilizar-se uma classe ou interface que utiliza tipos genéricos basta enviar no argumento de tipo o tipo desejado. No código abaixo String
foi definido como o tipo na variável de referência (Objeto<String> obj
) e na chamada do construtor (new Objeto<String>()
).
Objeto<String> obj = new Objeto<String>();
obj.setObjeto("Uma string qualquer.");
System.out.println(obj.getObjeto());
Uma string qualquer.
Pode-se definir mais de um tipo de parâmetro. Para tal, é necessário separá-los com vírgula e não utilizar o mesmo tipo já utilizado.
class Par<K, T> {
K chave;
T objeto;
Par(K chave, T objeto){
this.chave = chave;
this.objeto = objeto;
}
T getObjeto() {
return objeto;
}
K getChave() {
return chave;
}
void setObjeto(T objeto) {
this.objeto = objeto;
}
}
class Teste {
public static void main(String[] args) {
Par<Integer, String> par = new Par<Integer, String>(42, "É a pergunta?");
par.setObjeto("é a resposta para o universo.");
System.out.print(par.getChave() + " " + par.getObjeto() + "\n");
}
}
42 é a resposta para o universo.
A partir do Java SE 7 pode-se utilizar o operador diamante, que nada mais é que o argumento de tipo sem tipo especificado na chamada do construtor. Em tempo de compilação o argumento de tipo do construtor será inserido em correspondência ao argumento de tipo do tipo da variável.
Par<Integer, String> teste = new Par<>();
É possível definir uma classe ou interface com tipos genéricos em parâmetros de tipo.
Par<Integer, Objeto<String>> par = new Par<>(52, new Objeto<>());
par.getObjeto().setObjeto("objeto");
System.out.println(par.getChave() + ": " + par.getObjeto().getObjeto() + "\n");
52: objeto
É possível repassar o parâmetro de tipo definido em uma classe como argumento de tipo para referências de tipo genérico. No código abaixo a classe Colecao foi definida com o tipo T em seu parâmetro de tipo. O tipo T é então utilizado como argumento de tipo da variável de referência List<T> lista
.
class Colecao<T> {
List<T> lista;
List<T> getLista() {
return lista;
}
void setLista(List<T> lista) {
this.lista = lista;
}
}
class Teste {
public static void main(String[] args) {
Colecao<Double> colecao = new Colecao<>();
colecao.setLista(Arrays.asList(5.4, 3.43));
System.out.println(colecao.getLista());
}
}
[5.4, 3.43]
Parâmetro de tipo limitado[editar | editar código-fonte]
É possível substituir T por java.lang.Object
nos códigos da subseção anterior, logo, a única vantagem é a eliminação de cast no retorno de uma chamada ao método getObjeto.
Um benefício maior na utilização de Genéricos se dá ao especificar-se uma restrição aos tipos de parâmetro possíveis. É possível obter essa restrição definindo T como subclasse de determinada classe ou como implementador de determinada interface com a utilização da instrução extends
.
No código abaixo, T é restringido à CharSequence ou suas classes implementadoras.
class Teste<T extends CharSequence> {
public static void main(String[] args) {
Teste<String> teste = new Teste<>();
String palavra = teste.metodo("Exemplo");
System.out.println(palavra);
}
T metodo (T t) {
System.out.println(t.subSequence(0, 2));
return t;
}
}
Ex Exemplo
Caso utilizado um tipo diferente na linha 7, como metodo(5);
teria-se o erro de compilação abaixo.
Teste.java:7: error: method metodo in class Teste cannot be applied to given types; String palavra = metodo(5); ^ required: T found: int reason: inferred type does not conform to upper bound(s) inferred: Integer upper bound(s): CharSequence where T is a type-variable: T extends CharSequence declared in method <T>metodo(T) 1 error
Inferência de tipo em métodos[editar | editar código-fonte]
Alternativa à inferência de tipo no parâmetro de tipo da classe, é possível definir um parâmetro de tipo apenas para um método.
Para isso basta inserir o parâmetro de tipo antes do tipo de retorno.
<T> void metodo () {}
No código abaixo o tipo genérico, definido como T
, é utilizado no tipo de retorno e no tipo do parâmetro na lista de parâmetros da declaração de método. Em tempo de compilação, o tipo T
será convertido no tipo do argumento do método chamador, no caso abaixo como o argumento do método é do tipo String
("Exemplo") logo o tipo definido será String
.
class Teste {
public static void main(String[] args) {
String palavra = Teste.metodo("Exemplo");
System.out.println(palavra);
}
static <T> T metodo (T t) {
return t;
}
}
Exemplo
Tipo Cru[editar | editar código-fonte]
Para manter-se a compatibilidade com versões anteriores ao Java SE 5, a partir do lançamento da referida versão foi permitido que argumentos de tipo possam ser omitidos, são os chamados Tipos Crus.
//Tipo parametrizado.
Set<Double> conjunto;
//Tipo cru.
Set conjunto;
Em tempo de compilação os tipos crus são convertidos para o tipo Object
.
List lista = new ArrayList();
lista.add(5.5);
int i = (int) lista.get(0);
System.out.println(i);
5.5
Caso não houvesse o cast (int)
na linha 9 o seguinte erro seria exibido.
Teste.java:9: error: incompatible types: Object cannot be converted to int int i = lista.get(0); ^ Note: Teste.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.