Chapitre 6 de la spécification du langage Java (notamment Section 6.5 ) explique en détail comment Java détermine la signification d'un identifiant particulier dans un contexte particulier. Les règles sont assez complexes, mais, grosso modo, Java dispose de six espaces de noms :
- Noms des paquets
- noms des types
- noms des champs (variables)
- noms des méthodes
- les noms des variables locales (y compris les paramètres)
- étiquettes
Le même identifiant peut être utilisé pour des entités dans chacun de ces espaces de noms. Notez que les noms de types (classes) et les noms de champs vivent séparément, ce qui explique pourquoi votre code est légal.
Les noms hérités dans le même espace de noms peuvent aussi parfois être ombrés ou masqués. Il arrive qu'un identificateur soit ambigu ; il doit alors être qualifié d'une manière ou d'une autre (par exemple, avec un nom de paquetage), faute de quoi le compilateur se plaindra.
Les obscurcisseurs de code utilisent cela à bon escient, au point que l'on peut se retrouver avec un paquetage nommé a.a
et une classe nommée a
dans le paquet a
(qui serait également identifié comme a.a
). Sans oublier que les mots clés de Java tels que do
y for
sont des noms légaux dans les fichiers .class (mais pas dans le code source Java). Cela fait de la rétro-ingénierie un véritable casse-tête.