web-dev-qa-db-fra.com

Sensibilité de cas de Java noms de classe

Si l'on écrit deux publiques Java classes avec le même nom insensible à un même cas-insensible dans différents répertoires, les deux classes ne sont pas utilisables au moment de l'exécution. (J'ai testé cela sous Windows, Mac et Linux avec plusieurs versions de la Hotspot jvm. Je ne serais pas surpris s'il y avait d'autres JVM où ils sont utilisables simultanément.) Par exemple, si je crée une classe nommée a et une nommée A comme:

// lowercase/src/testcase/a.Java
package testcase;
public class a {
    public static String myCase() {
        return "lower";
    }
}

// uppercase/src/testcase/A.Java 
package testcase;
public class A {
    public static String myCase() {
        return "upper";
    }
}

Trois projets Eclipse contenant le code ci-dessus sont disponibles sur mon site Web .

Si essayez, j'appelle myCase sur les deux classes comme si:

System.out.println(A.myCase());
System.out.println(a.myCase());

Le typechecker réussit, mais lorsque je gère le fichier de classe générer par le code directement au-dessus, je reçois:

Exception dans le fil "Main" Java.Lang.NocLassDEffoundEfferror: TestCase/A (Mauvais nom: TestCase/A)

En Java, les noms sont en général sensibles à la casse. Certains systèmes de fichiers (par exemple Windows) sont insensibles de cas, donc je ne suis pas surpris que le comportement ci-dessus se produise, mais il semble faux. Malheureusement, le Java Spécifications est étrangement non commit sur quelles classes sont visibles. La Spécification de la langue Java (JLS), Java SE 7 édition (section 6.6.1, page 166) dit:

Si un type de classe ou d'interface est déclaré public, il se peut que tout code, à condition que l'unité de compilation (§7.3) dans laquelle elle soit déclarée soit observable.

Dans la section 7.3, la JLS définit l'observabilité d'une unité de compilation en termes extrêmement vagues:

Toutes les unités de compilation du paquet prédéfini Java et ses sous-correspondances Lang et Io sont toujours observables. Pour tous les autres packages, Le système hôte détermine quelles unités de compilation sont Observable .

Le Spécification de la machine virtuelle Java est similaire vague (section 5.3.1):

Les étapes suivantes sont utilisées pour charger et créer ainsi la classe NONARRAY ou l'interface C désignée par [Nom binaire] N à l'aide du bootstrap Chargeur de classe [...] sinon, le Java machine virtuelle passe l'argument n à une invocation d'une méthode sur le bootstrap Chargeuse de classe pour rechercher une représentation prétendue de C de manière dépendante de la plate-forme.

Tout cela conduit à quatre questions à l'ordre décroissant d'importance:

  1. Y a-t-il des garanties sur lesquelles les classes sont chargées par le chargeur de classe par défaut dans chaque JVM? En d'autres termes, puis-je mettre en œuvre un JVM valide, mais dégénéré, qui ne chargera aucune classe, à l'exception de ceux de Java.lang et Java.io?
  2. S'il y a des garanties, le comportement dans l'exemple ci-dessus violera-t-il la garantie (c'est-à-dire le comportement d'un bug)?
  3. Y a-t-il un moyen de faire hotspot charge a et A simultanément? Ecrire un travail de chargeur de classe personnalisé?
47
Josh Sunshine
  • Y a-t-il des garanties sur lesquelles les classes sont chargeuses par bootstrap classe chargeuse dans chaque JVM?

Les bits de noyau et les morceaux de la langue, ainsi que des classes de mise en œuvre de soutien. Pas garanti d'inclure une classe que vous écrivez. (La JVM normale charge vos classes dans un chargeur de classé séparé du bootstrap un, et en fait la normale bootstrap Chargeur charge ses classes d'un pot normalement, Comme cela produit un déploiement plus efficace qu'une grande ancienne structure de répertoires remplie de classes.)

  • S'il y a des garanties, le comportement dans l'exemple ci-dessus violera-t-il la garantie (c'est-à-dire le comportement d'un bug)?
  • Y a-t-il un moyen de faire de "standard" de charge JVMS et une simultanément? Ecrire un travail de chargeur de classe personnalisé?

Java charge des cours en mappant le nom complet de la classe dans un nom de fichier qui est ensuite recherché sur la classe de classe. Ainsi testcase.a va à testcase/a.class et testcase.A va à testcase/A.class. Certains systèmes de fichiers mixent ces choses et peuvent servir l'autre lorsque l'on est demandé. D'autres sont corrects (en particulier, la variante du format zip utilisé dans les fichiers JAR est entièrement sensible à la casse et portable). Il n'y a rien que =Java peut faire à ce sujet (bien que an IDE peut le gérer pour vous en gardant le .class Les fichiers à l'écart des natifs fs, je ne sais pas si vous le faites et le JDK javac le plus certainement n'est certainement pas si intelligent).

Cependant, ce n'est pas le seul point à noter ici: les dossiers de classe connaissent en interne quelle classe ils parlent. L'absence du de la classe attendue du fichier signifie simplement que la charge échoue, ce qui entraîne le NoClassDefFoundError que vous avez reçu. Ce que vous avez eu était un problème (un mauvais déploiement dans au moins un peu de sens) qui a été détecté et traité de manière robuste. Théoriquement, vous pouvez construire un chargeur de classe qui pourrait gérer de telles choses en maintenant la recherche, mais Pourquoi déranger? Mettre les fichiers de classe à l'intérieur d'un pot va réparer les choses beaucoup plus robustement; ceux-ci sont traités correctement.

Plus généralement, si vous rencontrez un lot réel, prenez à faire des constructions de production sur une Unix avec un système de fichiers sensible à la casse (un système CI comme Jenkins est recommandé) et Quels développeurs nomment des cours avec juste des différences de cas et les faire arrêter car il est très déroutant!

19
Donal Fellows

La belle explication du donal laisse peu à ajouter, mais laissez-moi brièvement muse sur cette phrase:

... Java classes avec le même nom insensible à l'insensible ...

Les noms et les chaînes en général ne sont jamais insensibles de cas en eux-mêmes, ce n'est que là-là interprétation qui peut être. Et deuxièmement, Java ne fait pas une telle interprétation.

Donc, une phrase correcte de ce que vous aviez en tête serait:

... Java classes dont les représentations de fichiers dans un système de fichier insensible à casse ont des noms identiques ...

1
Tillmann