Réflexion (informatique)
En programmation informatique, la réflexion est la capacité d'un programme à examiner, et éventuellement à modifier, ses propres structures internes de haut niveau lors de son exécution.
On appelle réflexivité le fait pour un langage de programmation de permettre l'écriture de tels programmes. Un tel langage de programmation est dit réflexif.
Introspection et intercession
On distingue deux techniques utilisées par les systèmes réflexifs :
- l'introspection, qui est la capacité d'un programme à examiner son propre état ;
- l'intercession, qui est la capacité d'un programme à modifier son propre état d'exécution ou d'altérer sa propre interprétation ou signification.
L'introspection est utilisée pour effectuer des mesures de performance, inspecter des modules ou déboguer un programme. Elle est implémentée dans des langages comme Smalltalk ou Java qui fournissent des outils pour connaître la classe d'un objet, ses attributs, ses méthodes, etc. L'introspection n'existe pas dans des langages comme C ou Pascal (mais existe dans Delphi depuis la version 2010).
L'intercession permet à un programme d'évoluer automatiquement en fonction des besoins et de l'environnement. Cette propriété apparaît dans des langages comme Smalltalk, Groovy ou Python, mais elle n'existe pas dans des langages comme C.
La réflexion, l’introspection et l'intercession sont des cas particuliers de métaprogrammation.
Réflexion structurelle et réflexion comportementale
Parallèlement aux concepts d'introspection et d'intercession, il existe deux types de réflexion : la réflexion structurelle (qui concerne surtout le code du programme) et la réflexion comportementale (qui concerne surtout l’environnement du programme).
La réflexion structurelle consiste à réifier le code d'un programme et tous les types abstraits accessibles par ce programme. Dans le premier cas, la réification du code d'un programme permet de manipuler ce programme pendant l'exécution. Il est possible ainsi de maintenir un programme même lorsque celui-ci effectue des tâches. Dans le deuxième cas, la réification des types abstraits permet au programme d'examiner et de modifier la structure de types complexes. On peut ainsi, par exemple, mettre au point des algorithmes génériques de sérialisation.
La réflexion comportementale (ou réflexion de comportement) concerne plus particulièrement l'exécution du programme et l'environnement du programme. Par ce type de réflexion, un programme peut « savoir » comment il est interprété et peut ainsi modifier sa façon d'être exécuté, en intervenant sur les structures de données de l'évaluateur du programme et sur l'évaluateur lui-même. De fait, le programme peut obtenir des informations sur son implémentation ou même s'auto-réorganiser afin de s'adapter au mieux à un « environnement ».
Réflexion et programmation objet
En programmation orientée objet, l'architecture réflexive est implémentée par le concept des métaobjets. Ceux-ci représentent des éléments des programmes orientés objets comme les classes, les messages et les fonctions génériques. La manipulation de ces métaobjets se fait par un protocole à métaobjets qui permet de décider des comportements du langage. CLOS est le premier langage à avoir implémenté un protocole à méta-objets.
Langages réflexifs
Les langages suivants sont réflexifs :
- Qt (bibliothèque C++)
- Smalltalk
- LOGO
- CLOS
- Python
- Tcl
- Ruby
- Io
- Java
- Objective-C
- PHP, depuis la version 4
- Lua
- les langages qui fonctionnent avec l'architecture .NET (comme le C#, le Visual Basic, le J# ou le C++/CLI)
- langages assembleurs
Dans les langages interprétés ou compilé à la volée avant exécution (Lisp par exemple), il n'y a pas de différence entre l'interprétation du code et la réflexion étant donné que c'est le code source qui évolue et non uniquement le code issu de la compilation.
Exemple
L'exemple suivant est écrit en Java :
// Sans utiliser la réflexion
Foo foo = new Foo();
foo.hello();
// En utilisant la réflexion
Class<?> cl = Class.forName("package.name.Foo");
// Instanciation de l'objet dont la méthode est à appeler
Object instance = cl.newInstance();
// Invocation de la méthode via réflexion
Method method = cl.getClass().getDeclaredMethod("hello", new Class<?>[0]);
method.invoke(instance);
L'exemple suivant est écrit en C# :
// Sans utiliser la réflexion
Foo foo = new Foo();
foo.hello();
// En utilisant la réflexion
Type type = typeof(Foo);
// Instanciation de l'objet dont la méthode est à appeler
// Il existe deux façons d'instancier un objet via la réflexion :
// 1, soit :
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
object instance = constructor.Invoke(new object[]{});
// 2, soit :
instance = Activator.CreateInstance(type);
// Invocation de la méthode via réflexion
MethodInfo method = type.GetMethod("hello");
method.Invoke(instance, new object[]{});
Les deux morceaux de code créent une instance de la classe Foo et appellent leur méthode hello.
Dans le premier programme, le nom des classes et des méthodes est codé en dur, il n'est pas possible d'utiliser le nom d'une autre classe. Dans le second programme, en revanche, le nom des classes et des méthodes peut varier à l'exécution.
Bibliographie
- D. G. Bobrow, R. G. Gabriel, and J. L. White. Object oriented programming: the CLOS perspective. MIT Press, 1993.
- G. Kickzales, J. des Rivières, and D. G. Bobrow. The art of the metaobject protocol. MIT Press, 1991.