Un switch annoté en Java
Un billet sur le blog de l’Agilitateur répondant à un billet sur le blog d’Excilys concernant les switchs en java me donne envie de parler un peu de codage en Java. Cet article est une variante du code C# proposé par Oaz en Java.
Je vais vous épargner toute la discussion sur le switch c'est bien / c'est nul, il y a déjà les commentaires enflammés sur le blog d'Excilys pour ça.
Passons directement au code : de la même manière qu'Oaz l'a fait, je dispose d'une interface Function qui définit le contrat que vont suivre les différents cas du switch. Pour que l'on sache que les différentes sous-classes sont bien des cases, on les annote avec un @Case créé pour l'occasion : On demande au compilateur de garder cette annotation au runtime (pour faire du scanning dynamique), et on précise qu'elle peut s'appliquer à un type (classe ou interface).
On peut alors utiliser l'annotation de cette façon :
Reste maintenant le plus dur à faire : scanner un package à la recherche de classes annotées @Case et qui héritent de notre classe de base (ici Function, mais on va essayer de rester génériques).
J'ai fait appel au generic type introduit en Java5, cela permet d'avoir un switch acceptant n'importe quelle classe de base qu'implémentent les différents cas. J'ai été obligé de repasser cette information dans le constructeur car en Java il n'est pas possible de faire un T.class dans la phase de scan des classes (pour savoir si la classe scannée étend ou implémente T). À la place, on a donc un Class La méthode scanCases va parcourir toutes les classes d'un package (grâce à la méthode getClasses() honteusement trouvée sur le forum de Dernière étape : la méthode on() qui renvoie l'instance associée à un cas ou jette une exception s'il n'existe pas. La glue entre tout ça, c'est un main assez simple :
Notez le dernier appel qui va planter : j'ai bien une classe annotée @Case(name="othercase") mais elle n'implémente pas Function (une ruse de sioux pour égarer le lecteur peu attentif ;-) ). Conclusion : c'est aussi possible en Java, avec un peu de gymnastique intellectuelle pour arriver à faire ce qu'on souhaite avec les generics :PSun Oracle). Pour chaque classe, on regarde si elle est annotée @Case et si elle étend ou implémente T. Si c'est le cas, on en ajoute une instance dans une Map avec le nom du cas comme clé.