A declaração for-each
A partir do Java 5.0, os desenvolvedores Java tiveram um novo tipo de loop for aprimorado a sua disposição, projetado especificamente para iterar sobre matrizes e coleção de objetos. Este loop for aprimorado, para maior clareza, vamos nos referir como loop for-each , é mostrado na Figura 2.8.
A declaração de loop for-each é composta de uma seção de inicialização e um objeto para ser iterado. O lado direito da declaração do loop for-each deve ser um array Java embutido ou um objeto cuja classe implementa java.lang.Iterable, que inclui a maioria dos frameworks Java Collections. O lado esquerdo do loop for-each deve incluir um declaração para uma instância de uma variável, cujo tipo corresponde ao tipo de um membro da matriz ou coleção no lado direito da instrução. Em cada iteração do loop, a variável nomeada no lado esquerdo da instrução é atribuída um novo valor da matriz ou coleção do lado direito da declaração.
Para o exame OCA, os únicos membros da estrutura de Coleções que você precisa estar ciente são List e ArrayList. Neste capítulo, vamos mostrar como iterar sobre objetos List, e no Capítulo 3 entraremos em detalhes sobre como criar objetos List e como eles diferem do Java de matrizes tradicional .
Vamos rever alguns exemplos:
■O que esse código irá gerar?
final String[] names = new String[3];
names[0] = "Lisa";
names[1] = "Kevin";
names[2] = "Roger";
for(String name : names) {
System.out.print(name + ", ");
}
Este código irá compilar e imprimir:
Lisa, Kevin, Roger,
■ O que esse código irá gerar?
java.util.List<String> values = new java.util.ArrayList<String>();
values.add("Lisa");
values.add("Kevin");
values.add("Roger");
for(String value : values) {
System.out.print(value + ", ");
}
Este código irá compilar e imprimir os mesmos valores:
Lisa, Kevin, Roger,
Quando você ver um loop for-each no exame, verifique se o lado direito é um array ou objeto iterável e o lado esquerdo tem um tipo correspondente. Por exemplo, os dois os exemplos a seguir não serão compilados.
■ Por que os seguintes itens falharão em compilar?
String names = "Lisa";
for(String name : names) { // DOES NOT COMPILE
System.out.print(name + " ");
}
Neste exemplo, names do tipo String não são uma matriz nem implementam java.lang.Iterable, então o compilador lançará uma exceção, pois não sabe como iterar.
■ Por que os seguintes itens falharão em compilar?
String[] names = new String[3];
for(int name : names) { // DOES NOT COMPILE
System.out.print(name + " ");
}
Este código não será compilado porque o lado esquerdo da instrução for-each não define uma instância de String . Observe que neste último exemplo, o array é inicial com três valores de ponteiro nulo . Por si só, isso não fará com que o código não compile, como um loop corrigido seria apenas saída nula três vezes.
Comparando for e for-each
Já que for e for-each usam a mesma palavra-chave, você pode estar se perguntando como eles são relacionados. Enquanto esta discussão está fora do escopo para o exame, vamos dar um tempo para explorar como os loops for-each são convertidos em loops for pelo compilador.
Quando for-each foi introduzido no Java 5, foi adicionado como um aprimoramento em tempo de compilação. Isto significa que, na verdade, Java converte o for-each em um padrão de circuito durante a compilação. Por exemplo, assumir names é uma matriz de String[] como vimos no primeiro. Por exemplo, os dois loops a seguir são equivalentes:
for(String name : names) {
System.out.print(name + ", ");
}
for(int i=0; i < names.length; i++) {
String name = names[i];
System.out.print(name + ", ");
}
Para objetos que herdam java.lang.Iterable, há uma conversão diferente, mas semelhante. Por exemplo, assumir valores é uma instância de List <Integer> , como vimos no segundo por exemplo, os dois loops a seguir são equivalentes:
for(int value : values) {
System.out.print(value + ", ");
}
for(java.util.Iterator<Integer> i = values.iterator(); i.hasNext(); ) {
int value = i.next();
System.out.print(value + ", ");
}
Observe que na segunda versão, não há uma instrução de atualização , pois não é necessária quando usando a classe java.util.Iterator .
Você deve ter notado que nos exemplos anteriores para cada um, havia um vírgula extra impressa no final da lista:
Lisa, Kevin, Roger,
Embora a instrução for-each seja conveniente para trabalhar com listas em muitos casos, ela oculta o acesso à variável do iterador de loop. Se quiséssemos imprimir apenas a vírgula entre nomes, que poderia converter o exemplo em um padrão de loop, como no exemplo a seguir:
java.util.List<String> names = new java.util.ArrayList<String>();
names.add("Lisa");
names.add("Kevin");
names.add("Roger");
for(int i=0; i<names.size(); i++) {
String name = names.get(i);
if(i>0) {
System.out.print(", ");
}
System.out.print(name);
}
Este código de exemplo produziria o seguinte:
Lisa, Kevin, Roger
Também é comum usar um padrão loop for em um loop for-each se comparar múltiplos elementos em um loop dentro de uma única iteração, como no exemplo a seguir. Note que pulamos a execução do primeiro loop, já que o valor [-1] não está definido e lançaria um erro IndexOutOfBoundsException.
int[] values = new int[3];
values[0] = 10;
values[1] = new Integer(5);
values[2] = 15;
for(int i=1; i<values.length; i++) {
System.out.print(values[i]-values[i-1]);
}
Este código de exemplo produziria o seguinte:
-5, 10,
Apesar desses exemplos, os loops aprimorados for-each são bastante úteis em Java em uma variedade de circunstâncias. Como um desenvolvedor, porém, você sempre pode reverter para um padrão loop for se você precisa de um controle fino.