La réflexion
Introduction
Nous allons maintenant nous intéresser à une autre partie de la programmation objet dénommée - avec plus ou moins de réussite - la réflexion. La réflexion en POO n’est pas le fait de réfléchir profondément à quelque chose. Non, il s’agit ici d’une radioscopie d’objets inconnus et compilés, qui va nous permettre de découvrir à la volée leurs propriétés et leurs comportements.
Il faut donc plutôt associer « reflet » plutôt que « méditation » à cette « réflexion »…
Mais pour quoi faire ?
L’interrogation est légitime : pourquoi avons-nous besoin de récupérer dynamiquement les caractéristiques d’objets ?
Nous allons voir deux exemples :
Le « plug-in »
Vous avez conçu une application ludique permettant aux petits et grands de se cultiver. L’application permet au joueur de s’identifier, de choisir un sujet à travailler et… de s’amuser (le plus important pour apprendre). L’application mesure alors des temps d’apprentissage, d’exercices, comptabilise des scores, les compare et délivre même des récompenses, etc. Un succès !
Les modules pédagogiques ne sont pas encore tous développés, car vous avez besoin du concours de spécialistes en géographie, en histoire, en mathématiques, etc. Les joueurs se verront proposer en téléchargement ces nouveaux modules au fur et à mesure de leur disponibilité. Une fois récupérés, votre application devra les reconnaître et les intégrer alors même qu’elle en ignorait l’existence quelques secondes plus tôt ! Mais alors comment ?
La réflexion sera une solution élégante, car elle permettra à votre application de parcourir le répertoire de téléchargement, de « radioscoper »...
Introspection d’une classe Java
Grâce à la réflexion, nous allons pouvoir récupérer dynamiquement depuis un objet :
-
le nom de la classe qui est à son origine ;
-
si cette classe est d’accès public, privé, etc. ;
-
quelle est sa classe parent ;
-
quelles interfaces sont supportées par cette classe ;
-
son ou ses constructeurs ;
-
ses méthodes ;
-
ses propriétés ;
-
des informations sur son package d’appartenance et ses commentaires ;
-
etc.
On parle ici de métadonnées, c’est-à-dire d’informations décrivant un type d’objet.
Les environnements de développement utilisent la réflexion pour vous assister dans l’écriture de vos programmes.
C’est la classe java.Lang.Class qui joue le rôle de collecteur d’informations entre un type d’objet et votre programme. Tous les objets Java - y compris les plus primitifs - appartiennent à une java.Lang.Class et on va tout de suite radioscoper celle de String et lui demander la liste de toutes ses méthodes.
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
Class stringClass = String.class;
Method[] methods = stringClass.getMethods(); ...
Chargement dynamique et utilisation d’une classe découverte
La première étape de notre projet consiste à définir le ticket d’entrée à notre super système de plug-in, à savoir un contrat que tout module devra implémenter pour prétendre être appelé.
Pour cela, nous allons créer un package contenant uniquement une interface comme celle-ci :
public interface IPlugin {
String VoilaMonNom();
Boolean JoueAvecMoi();
}
Nous allons transformer le fichier .class généré en sortie de ce projet par un fichier .jar qui est le conteneur de livraison des fichiers Java compilés.
Pour cela, nous allons activer le menu File, option Project Structure, sélectionner Artifacts, puis, en cliquant sur le bouton +, ajouter un JAR contenant notre module et ses dépendances.
Cliquez sur Create Manifest.
Sélectionnez le répertoire contenant ce projet puis cliquez sur OK.
Entrez IPlugin comme Main Class puis cliquez sur OK.
Construisez le fichier .jar en utilisant le menu Build, option Build Artifacts.
La première étape est maintenant terminée : nous avons un fichier interface_plugin.jar contenant le contrat. Le projet figure dans le répertoire Chap10\interface_plugin du .zip accompagnant cet ouvrage. Nous allons maintenant nous placer côté créateur de plug-ins et créer une classe qui va implémenter cette interface.
Dans ce nouveau projet, pour pouvoir écrire une classe qui implémente notre interface, il faut tout d’abord lui ajouter sa librairie.
Pour cela, nous allons activer le menu File, option Project Structure, sélectionner Libraries et, en cliquant sur le bouton +, ajouter la librairie Java.
Sélectionnez la librairie interface_plugin.jar générée à l’étape précédente puis cliquez sur OK.
Cliquez sur OK pour valider l’ajout de la librairie au module plugin_informatique.
Cliquez sur OK pour fermer l’interface Project Structure.
Nous pouvons maintenant écrire le code de notre plug-in :
import java.io.IOException;
public class plugin_informatique implements IPlugin { ...
Exercice
Créez un nouveau projet contenant une classe implémentant l’interface IPlugin. Recopiez son fichier .jar dans le répertoire des plug-ins utilisé précédemment et vérifiez qu’il est bien reconnu et exploité par l’application chargeur_plugins.
Voici un exemple console de ce que cela pourrait donner :
Ce projet figure dans le répertoire Chap10\plugin_musique du .zip accompagnant cet ouvrage.
Privé, mais pas tant…
Nous avons vu qu’il fallait masquer, à grand renfort d’attributs private, certains membres de nos classes pour les protéger d’utilisations malvenues d’autres développeurs. Tout cela est bien remis en question avec la réflexion !
Ajoutez à un des plug-ins précédemment développés la méthode suivante :
private void IciCestPrivate(){
System.out.println("Cet accès est pourtant interdit");
}
Ajoutez au gestionnaire de plug-ins, juste après les invocations des méthodes VoilaMonNom et JoueAvecMoi la séquence suivante :
Method privateMeth = o.getClass().getDeclaredMethod("IciCestPrivate");
if( privateMeth != null) {
privateMeth.setAccessible(true);
privateMeth.invoke(o);
}
Relancez l’application et constatez.
La réflexion est décidément puissante et permissive, mais ce n’est pas fini… En plus de découvrir les membres des classes, elle nous permet également de lire le code source.
Décompilation et obfuscation
Même si cela sort un peu du cadre de ce livre, cette dernière section traite de la protection de nos développements contre la copie. Comme évoqué précédemment, les .jar et autres .class peuvent être facilement convertis en code source Java par des outils adéquats. Ces outils sont appelés des décompilateurs. Ils se présentent sous forme d’applications à installer sur votre machine ou encore sous forme de sites Internet. Nous allons utiliser l’un d’eux : http://www.javadecompilers.com
Rappelez-vous le petit exercice du chapitre Héritage et polymorphisme sur les comptes bancaires.
Nous allons tout d’abord prendre ses fichiers .class - donc fichiers binaires générés par IntelliJ IDEA - pour les passer au décrypteur en ligne.
Connectez-vous sur le site http://www.javadecompilers.com.
Sur l’écran d’accueil, cliquez sur le bouton Choisir un fichier ou faites glisser l’un des fichiers .class dans cette zone.
Cliquez sur Upload and Decompile (transférer et désassembler).
Au bout de quelques instants, le travail est terminé et vous pourrez cliquer sur le bouton Save.
Le code décompilé est déposé gracieusement sur votre PC dans un fichier .zip. À l’intérieur, le code source récupéré est celui-ci :
package labcomptebancaire;
...