Récupérer les noms de méthodes en Java : l'astuce SerializedLambda
Extraire les noms de méthodes en Java : le trick malin du SerializedLambda
Tu bosses avec desparsers CSV, des frameworks ORM ou des clients REST en Java ? Alors tu connais ce problème agaçant : tu dois passer un nom de champ en String, mais coder en dur des trucs comme "auteur" ou "getTitre", c'est moche. Pas fiable, ça casse quand tu refactores, et ton code devient un vrai labyrinthe de chaînes magiques.
Et si tu pouvais faire plutôt ça ?
String auteur = csvContents.get(nameOf(Book::getAuteur));
String titre = csvContents.get(nameOf(Book::titre));
Tu passes la référence de méthode directement et Java se débrouille pour récupérer le nom. Comment ça marche ? On regarde.
Le problème
Java ne te donne pas nativement le nom d'une méthode à partir d'une référence. Quand tu écris Book::getAuteur, tu obtiens un objet Function<Book, String>, mais aucune API intégrée ne te permet de demander : "Hé, c'est quoi le nom de ta méthode exactement ?"
Tu pourrais croire que la réflexion peut aider :
Method[] methods = Book.class.getMethods();
String name = methods[0].getName(); // Ça marche, mais laquelle ?
Sauf que là, tu dois déjà savoir quelle méthode tu cherches — ce qui enlève tout l'intérêt.
Le secret du SerializedLambda
Voici où ça devient intéressant. Quand Java sérialise un lambda ou une référence de méthode, il ne se contente pas de balancer du bytecode opaque. Il crée un objet SerializedLambda qui contient les métadonnées de la méthode originale — y compris son nom.
Le trick, c'est d'intercepter ce processus de sérialisation avant qu'il ne se termine, et de capturer cet objet SerializedLambda.
Voici l'implémentation clé :
public static <T, R> String nameOf(final Getter<T, R> methodRef) {
return toSerializedLambda(methodRef).getImplMethodName();
}
private static <T, R> SerializedLambda toSerializedLambda(final Object methodRef) {
try {
try (CustomObjectOutputStream stream = new CustomObjectOutputStream()) {
stream.writeObject(methodRef);
return (SerializedLambda) stream.interceptedObject;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
On crée un ObjectOutputStream personnalisé qui intercepte le callback replaceObject. Quand Java tente de sérialiser notre référence de méthode, il appelle replaceObject avec le SerializedLambda — et on le capture à ce moment précis.
Pourquoi c'est utile
Cette technique ouvre la porte à des patterns vraiment sympas :
Configuration type-safe pour le traitement de données :
// Au lieu de :
dataMapper.map(book, "titre", "auteur");
// Tu peux écrire :
dataMapper.map(book, nameOf(Book::title), nameOf(Book::getAuteur));
Sécurité à la compilation pour les appels API :
// Requêtes en base qui refactorent proprement
List<Book> results = bookRepository.findBy(nameOf(Book::getAuteur), "Shakespeare");
Si tu renommes la méthode getAuteur(), le compilateur te signalera tous les appels nameOf(Book::getAuteur) à mettre à jour. Plus besoin de chercher les chaînes codées en dur.
Au-delà des getters simples
Le pattern de base fonctionne bien pour les méthodes sans argument, mais comment gérer des méthodes avec des paramètres ? Tu peux étendre l'approche en définissant des interfaces fonctionnelles sérialisables :
public interface BiFunction2<T1, T2, R> extends Serializable {
R apply(T1 t1, T2 t2);
}
public <T1, T2, R> String nameOf(final BiFunction2<T1, T2, R> methodRef) {
return toSerializedLambda(methodRef).getImplMethodName();
}
Ça te permet de gérer des méthodes comme BookService::findByTitreAndAuteur ou Repository::save.
Un mot de prudence
Si cette technique est puissante, garde ces points en tête :
- Performance : La sérialisation a un coût. N'appelle pas
nameOf()dans des boucles chaudes — appelle-le une fois lors de l'initialisation et mets le résultat en cache. - Requisitos de sérialisation : Tes interfaces fonctionnelles doivent étendre
Serializable. - Pas universel : Ça marche pour les références de méthodes assignables à des interfaces fonctionnelles. La réflexion directe reste nécessaire pour les méthodes arbitraires.
Des libraries qui font le job
Si implémenter tout ça te semble fastidieux, bonne nouvelle : c'est déjà fait. La library Safety Mirror propose des implémentations robustes et pousse même le concept plus loin en extrayant directement des objets java.lang.reflect.Method — te donnant les capacités complètes de réflexion avec de la sécurité de types.
En résumé
Les références de méthode en Java, c'est puissant, mais elles masquent délibérément les noms de méthodes. Des fois, t'as besoin de récupérer ces métadonnées. Le trick du SerializedLambda offre une solution propre et type-safe pour combler le fossé — te permettant d'écrire du code expressif, facile à refactorer, tout en gardant les chaînes magiques à distance.
Que tu construises un pipeline de traitement de données, un client API flexible, ou que tu sois simplement fatigué de chercher "getAuteur" dans ton codebase, cette technique pourrait être exactement ce qu'il te faut.
Tu as trouvé des usages créatifs pour les métadonnées des références de méthode dans tes projets ? Laisse un commentaire — on adore voir comment tu utilises ces patterns en production.