31 votes

Amélioré pour la compilation de boucle fine pour JDK 8 mais pas 7

Considérons l'extrait de code suivant, je stumpled après remaniement, lors de notre arrivée, pourquoi le serveur de build signalé une fracture de la construire, mais il était très bien dans mon IDE:

List<String> text;
...
for (String text : text) {...}

Ainsi, le même nom est utilisé pour la Chaîne et la Liste dans la for-each.

Bien sûr ce n'est pas très sage de faire, mais après la suite de mon nosiness avant de le renommer, j'ai vu que le code ci-dessus compile bien avec JDK 8, mais donne l'erreur ci-dessous avec JDK 7:

  error: for-each not applicable to expression type
        for (String text : text) {
                           ^
  required: array or java.lang.Iterable
  found:    String
1 error

Je sais que des modifications ont été apportées à plusieurs parties dans cette région à l'intérieur de la JDK - mais quelqu'un peut-il m'éclairer sur le pourquoi de ce comportement se produit?


Mise à jour: Depuis que j'ai quelques commentaires à propos des comportements différents, voici un exemple complet de la classe:

import java.util.Arrays;
import java.util.List;

public class Strange {

    List<String> text = Arrays.asList("Max", "Alex", "Maria");

    public static void main(String[] args) {
        new Strange().doSomething("Alex");
    }

    public void doSomething(String name) {
        for (String text : text) {
            System.out.println(text.equals("Alex"));
        }
    }

}

Et voici le processus de compilation et de sortie (Windows 7 64bit):

C:\copy>c:\Projects\java\jdk1.7.0_79\bin\javac.exe Strange.java
Strange.java:13: error: for-each not applicable to expression type
        for (String text : text) {
                           ^
  required: array or java.lang.Iterable
  found:    String
1 error

C:\copy>c:\Projects\java\jdk1.8.0_60\bin\javac.exe Strange.java

C:\copy>

Conclusion: J'étais tellement perplexe pourquoi mon IDE (qui utilise 8) ne se plaignent pas deux fois le même nom dans une même instruction - mais maintenant, il est clair que ce n'est pas une instruction. Je me demande vraiment pourquoi ce point a été si longtemps en place si la JLS en dispose autrement. Mais de toute façon, merci pour les idées que j'ai reçus et les réponses grands (ce qui fait qu'il est difficile pour moi de prendre la meilleure).

14voto

Tunaki Points 2663

Cela devrait en fait la compilation d'amende pour JDK 7 et 8.

Citant JLS section 14.14.2 (qui est le même pour Java 7 spécification):

Le renforcement de l'énoncé est équivalent à une base de l'énoncé de la forme:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
      {VariableModifier} TargetType Identifier =
          (TargetType) #i.next();
      Statement
}

La réécriture de l'enhanched pour la boucle avec Iterator

for (String text : text) {...}

devient

for (Iterator<String> it = text.iterator(); it.hasNext(); ) {
    String text = it.next();
}

Puis, citant l' exemple 6.4.1 de la JLS:

Des restrictions similaires sur l'occultation des membres par des variables locales a été jugé impraticable, parce que l'ajout d'un membre dans une super-classe peut causer des sous-classes pour renommer les variables locales. Considérations de faire des restrictions sur l'occultation de variables locales par les membres des classes imbriquées, ou sur l'occultation de variables locales par des variables locales déclarées dans les classes imbriquées peu attrayant ainsi.

En tant que tel, il n'y a pas d'erreur de compilation ici, car aucune restriction n'est faite lors de l'occultation d'une variable de membre par une variable locale, ce qui est le cas ici: la variable locale String text est l'occultation de la variable de membre List<String> text.

11voto

Holger Points 13789

Alors que le raisonnement, spécifié à l'aide de la traduction de l'amélioration du for boucle à la traditionnelle for boucle, utilisé par d'autres réponses est correcte, il est une spécification explicite de la portée:

§6.3. La portée d'une Déclaration

...

La portée d'une variable locale déclarée dans le FormalParameter partie de un renforcement for déclaration (§14.14.2) est le contenu de l' Énoncé.

(lien direct)

Ainsi, la portée de la variable ne comprend pas l' Expression de la renforcée for boucle...

Vous pouvez vérifier que cela n'a pas changé, par rapport à Java 7 et Java 6, si les deux (j'ai essayé de Java 6 javac) exposition le contredire comportement.

Donc, ce changement dans le comportement du compilateur est la correction d'un vieux bug...

6voto

Stephen C Points 255558

Je dirais que c'est un compilateur bug dans la version de Java 7 compilateur que vous utilisez.

Le plus tôt text est un champ, et il est légal pour l' text locale déclarée dans l' for déclaration à l'ombre d'un champ.

Puis on regarde ce que la boucle de moyens. Selon le JLS,

    for (String text : text) {...}

est équivalent à

    for (Iterator<String> #i = text.iterator(); #i.hasNext(); ) {
        String text = (String) #i.next();
        ...
    }

Comme vous pouvez le voir l'intérieur text n'est pas à la portée de l' text.iterator() expression.


J'ai essayé de chercher l'Oracle Java Bugs de Base de données, mais ne pouvait pas trouver quelque chose qui correspondait à ce scénario.

3voto

Klitos Kyriacou Points 456

Bien que je pense que les autres réponses sont correctes, permettez-moi d'être l'avocat du diable et offre le point de vue opposé.

Évidemment JDK 7 analyse de la boucle foreach de telle façon que la variable "texte" est également dans le champ d'application après le ':'. Pour tester cela, j'ai écrit la méthode suivante. Il compile et fonctionne très bien en Java 1.7:

public static void main(String[] args) {
    for (String text : new String[] {text = "hello", text, text, text})
        System.out.println(text);
}

Bien que d'autres l'ont dit c'est un bug dans le jdk 1.7 (et c'est probablement le cas), je ne pouvais pas trouver n'importe où dans le JLS qui dit spécifiquement de la variable déclarée n'est pas dans le champ d'application après le ':'. Si ce n'est pas un bug, Java 8 sauts de compatibilité.

2voto

Brad Mace Points 12173

Votre serveur de build peut être en train de compiler en utilisant un jdk différent de votre machine locale. (Pas seulement un numéro de version différent, mais une implémentation complètement différente.) Eclipse est celle qui utilise son propre compilateur, je crois pour faciliter son échange à chaud de code.

Utiliser le même nom pour la collection et l'élément devrait poser des problèmes n'importe où , mais j'ai entendu parler et parfois remarqué qu'Eclipse tolérait des choses que le JDK Sun / Oracle ne tolérerait pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X