Class
est le point d'entrée de l'exploration du contenu d'une classe par introspection. On peut obtenir une instance de
Class
de trois façons :
getClass()
sur n'importe quel objet ;
String.class
ou
Marin.class
sont des objets instances de
Class
;
Class.forName(String)
, à laquelle on passe le nom complet d'une classe, avec le nom du package dans lequel elle se trouve.
ClassLoader
. On peut obtenir une référence sur le
class loader
qui a chargé une classe donnée en utilisant la méthode
getClassLoader()
. Certaines classes, chargées par un
class loader
particulier appelé
bootstrap class loader
ne retournent pas d'instance, mais
null
. C'est le cas notamment de toutes les classes des sous-packages de
java.*
.
Class
expose un jeu de méthodes qui permet d'otenir les constructeurs, les champs, et les méthodes de cette classe. Chacune de ces méthodes existe en quatre versions. Voyons-les pour l'accès aux champs d'une classe.
getFields()
: retourne la liste des champs publics de cette classe, et de ses super-classes ;
getDeclaredFields()
: retourne la liste des champs publics ou non, déclarés uniquement dans cette classe ;
getField(String name)
: retourne le champ dont le nom est précisé, s'il est public, et qu'il soit dans cette classe ou dans une super-classe ;
getDeclaredField(String name)
: retourne le champ dont le nom est précisé, qu'il soit public ou non, mais uniquement s'il se trouve dans cette classe.
null
dans cette API.
En résumé, on peut demander la liste des champs, constructeurs ou méthodes, qui est retournée dans un tableau. On peut demander un membre particulier de cette liste, en précisant son nom, et éventuellement les éléments pris en paramètre (cas des constructeurs et des méthodes). Enfin, soit on choisit de faire cette interrogation sur cette classe et ses super-classes, auquel cas on peut obtenir des membres publics, soit on la fait uniquement sur cette classe, sur tous ses membres, publics ou non.
Examinons maintenant toutes ces méthodes.
getAnnotations()
et
getAnnotation(Class<A> a)
: retournent le tableau des annotations posées sur cette classe, ou l'annotation de la classe passée en paramètre, si elle existe.
getDeclaredAnnotations()
: retourne le tableau de toutes les annotations de cette classes, éventuellement héritées des interfaces implémentées, ou des super-classes.
getConstructors()
et
getConstructor(Class<?>... types)
: retournent le tableau des constructeurs publics de cette classe, ou le constructeur qui prend en paramètre la liste des classes (donc des types) indiquée.
getDeclaredConstructors()
et
getDeclaredConstructor(Class<?>... types)
: retournent le tabeau des constructeurs de cette classes, publics ou non, ou le constructeur qui prend en paramètre la liste des classes (donc des types) indiquée.
getFields()
et
getField(String field)
: retournent le tableau des champs publics de cette classe, ou le champ dont le nom est passé en paramètre. Les champs publics des super-classes sont présents dans la liste.
getDeclaredFields()
et
getDeclaredField(String field)
: retournent le tableau des champs déclarés dans cette classes seulement, publics ou non.
getMethods()
et
getMethod(String, Class<?>... types)
: retournent le tableau des méthodes publiques de cette classe, et de ses super-classes, ou la méthode qui correspond aux paramètres spécifiés.
getDeclaredMethods()
et
getDeclaredMethod(String, Class<?>... types)
: retournent le tableau des méthodes de cette classe, publiques ou non, ou la méthode qui correspond aux paramètres précisés.
getCanonicalName()
: retourne le nom complet de cette classe, s'il existe. Certaines classes n'ont pas de nom, notamment les classes anonymes (c'est d'ailleurs la raison pour laquelle on les appelle anonymes !).
getName()
: retourne le nom du type de cette classe (
class
,
interface
, type primitif ou tableau).
getSimpleName()
: retourne le nom de cette classe, sans son nom de package.
getPackage()
: retourne une instance de la classe
Package
, qui modèlise le package dans lequel se trouve cette classe.
getSuperClass()
: retourne la super-classe de cette classe, sous forme d'une instance de
Class
.
getInterfaces()
: retourne un tableau des interfaces implémentées par cette classe, sous forme d'instances de
Class
.
Method
,
Field
et
Constructor
étendent toutes les trois la classe
AccessibleObject
. Cette classe concrète expose notamment une propriété :
accessible
, de type booléen, en lecture et en écriture.
Ce booléen permet d'activer ou non la vérification par la machine Java des droits d'accès sur l'objet considéré, de type
Method
,
Field
ou
Constructor
.
Il est à
false
par défaut, ce qui signifie que la machine Java vérifie ces droits d'accès. On peut le mettre à
true
, ce qui a pour effet de supprimer cette vérification, et de rendre utilisables les membres privés d'une classe, de toute une application.
Notons bien que positionner ce booléen à
true
pour un champ (par exemple), ne rend pas ce champ
public
s'il était
private
. On peut s'en rendre compte si l'on interroge la méthode
getModifiers()
de ce champ une fois la propriété
accessible
changée.
Nous verrons un exemple d'application dans la suite.
Class
expose également un jeu de méthodes qui permettent de tester à quel type de classe nous avons affaire. Ce type peut être de différentes natures. Voyons ces méthodes.
isInterface()
: retourne
true
si cette classe est une interface.
isEnum()
: retourne
true
si cette classe est une énumération.
isLocalClass()
et
isMemberClass()
: retourne
true
s'il s'agit d'une classe locale ou membre, respectivement.
isAnonymousClass()
: retourne
true
si cette classe est anonyme.
isPrimitive()
: retourne
true
si cette classe modélise un type primitif. Notons que tous les types primitifs sont associés à des classes :
int.class
,
float.class
, etc...
isArray()
: retourne
true
si cette classe modélise un tableau.
isSynthetic()
: retourne
true
si cette classe est une classe synthétique. Une classe synthétique est une classe créée dynamiquement, par le compilateur ou à l'exécution du code. Une classe synthétique est par exemple associée à chaque clause
switch
.
Class
expose une méthode
newInstance()
qui permet de créer une nouvelle instance de cette classe. Cette méthode invoque le constructeur vide de cette classe, qui doit donc exister.
L'utilisation de cette méthode permet, simplement à partir du nom d'une classe sous forme d'une chaîne de caractères (
String
), qui peut-être lue dans un fichier par exemple, de créer des instances de cette classe.
Exemple 47. Création d'un objet par introspection
// dans une méthode main String className = "org.paumard.model.Marin" ; // chargement d'une classe à partir de son nom // jette une exception du type ClassNotFoundException Class<?> marinClass = Class.forName(className) ; // instanciation d'un objet à partir de sa classe // jette deux exceptions : IllegalAccessException, InstantiationException // l'objet o est en fait de type org.paumard.model.Marin Object o = marinClass.newInstance() ;
isInstance(Object o)
: retourne
true
si cet objet est une instance de cette classe. Rappelons que cette classe peut modéliser une interface, ou une des super-classes de l'objet.
isAssignableFrom(Class<?> clazz)
: retourne
true
si cette classe ou interface est une super-classe, une super-interface, ou une classe qui implémente, directement ou non, la classe passée en paramètre. Dans le cas de classes modélisant les types primitifs, cette méthode ne retourne
true
que si les deux types sont identiques. Effectivement, les classes qui modélisent les types primitifs sont particulières : elles n'étendent pas
Object
, et n'implémentent aucune interface.
isAssignableFrom(Class<?>
signifie que l'on peut instancier cette classe en construisant une instance de la classe passée en paramètre. Elle est un peu délicate à utiliser, voyons un exemple. Considérons deux classes :
Marin
et
Capitaine
. La classe
Marin
implémente
Serializable
, et la classe
Capitaine
étend
Marin
.
Exemple 48. Utilisation de
Class.isAssignableFrom(Class)
// dans un fichier Marin.java public class Marin implements Serializable { // contenu de la classe } // dans un fichier Capitaine.java public class Capitaine extends Marin { // contenu de la classe } // dans une méthode main() boolean b1 = Capitaine.class.isAssignableFrom(Marin.class) ; // b1 est false boolean b2 = Marin.class.isAssignableFrom(Capitaine.class) ; // b2 est true boolean b3 = Capitaine.class.isAssignableFrom(Serializable.class) ; // b3 est false boolean b4 = Serializable.class.isAssignableFrom(Capitaine.class) ; // b4 est true
Exemple 49. Introspection d'une énumération : définition de l'
enum
public enum Grade { MOUSSE, CRABE, CHOUFFE, BOSCO, PACHA }
Exemple 50. Introspection d'une énumération : découvertes des valeurs constantes
public static void main(String[] args) { Grade[] grades = Grade.class.getEnumConstants() ; System.out.println(Arrays.toString(grades)) ; }
[MOUSSE, CRABE, CHOUFFE, BOSCO, PACHA]