Hacking the Method Name | Programming Thoughts
Inspired by a recent post on easily obtaining method parameter name by identifier, here is a technique to obtain a method’s name by method reference.
Imagine you have a class with getters that follow typical Java Bean naming convention or Java Record naming convention:
class Book {<br>...<br>public String getAuthor() { ... }<br>public String title() { ... }
How can we easily obtain "title" or "author" strings? Perhaps you want to directly parse CSV content, database results, or call a REST API:
String title = csvContents.get(nameOf(Book::title));<br>String author = dbQueryResults.get(beanNameOf(Book::getAuthor));<br>ListBook> books = bookClient.findByAttr(singletonMap(nameOf(Book::getAuthor), "Shakespeare"));
Solution
We can implement such a nameOf(...) functionality in Java 8+ by serializing a method reference to SerializedLambda which lets us easily read the method’s name:
Declare a serializable getter functional interface: interface Getter extends Function, Serializable {}
When serializing it, Java will create a SerializedLambda as a replacement object.
Intercept that SerializedLambda object and read the method’s name
Here is the code:
import java.io.IOException;<br>import java.io.ObjectOutputStream;<br>import java.io.OutputStream;<br>import java.io.Serializable;<br>import java.lang.invoke.SerializedLambda;<br>import java.util.function.Function;
public class GetterTool {
@FunctionalInterface<br>public interface GetterT, R> extends FunctionT, R>, Serializable {}
public static T, R> String nameOf(final GetterT, R> methodRef) {<br>return toSerializedLambda(methodRef).getImplMethodName();
private static T, R> SerializedLambda toSerializedLambda(final Object methodRef) {<br>try {<br>try (final CustomObjectOutputStream stream = new CustomObjectOutputStream()) {<br>stream.writeObject(methodRef);<br>return (SerializedLambda) stream.interceptedObject;<br>} catch (final IOException e) {<br>throw new RuntimeException(e);
private static class CustomObjectOutputStream extends ObjectOutputStream {<br>private Object interceptedObject = null;
public CustomObjectOutputStream() throws IOException {<br>super(nullOutputStream());<br>enableReplaceObject(true);
@Override<br>protected Object replaceObject(final Object obj) {<br>if (this.interceptedObject == null) {<br>this.interceptedObject = obj;<br>return obj;
private static OutputStream nullOutputStream() {<br>return new OutputStream() {<br>@Override public void write(final int b) { }<br>@Override public void write(final byte[] b, final int off, final int len) { }<br>@Override public void close() { }<br>};
And we can use it like this:
@Test<br>void test() {<br>assertEquals("title", GetterTool.nameOf(Book::title));
After implementing nameOf(), it’s straightforward to implement further transformations like beanNameOf() to remove the get prefix.
Other methods
So this technique works for zero-argument getter methods but what if we want to obtain the name of other methods like:
List findByAttr(Map attr);<br>Book save(String title, String author);
Method references need to target a functional interface, so we would need to define corresponding serializable functional interfaces. Only then overloaded nameOf() methods can be defined and called:
// for methods like findByAttr(...)<br>public interface Function2T1, T2, R> extends Serializable {<br>R apply(T1 t1, T2 t2);
// for methods like save(...)<br>public interface Function3T1, T2, T3, R> extends Serializable {<br>R apply(T1 t1, T2 t2, T3 t3);
T1, T2, R> String nameOf(final Function2T1, T2, R> methodRef);<br>T1, T2, T3, R> String nameOf(final Function3T1, T2, T3, R> methodRef);
Very tedious! Fortunately, someone has done the tedious work and in fact made it better. We can easily obtain, not only the method name, but also the actual java.lang.reflect.Method:
Method m1 = toMethod(BookClient::save);<br>Method m2 = toMethod(BookClient::findByAttr);
Take a look at this library: https://github.com/Hervian/safety-mirror/tree/master#type-safe-method-creation
-->
Problem #
Inspired by a recent post on easily obtaining method parameter name by identifier, here is a technique to obtain a method’s name by method reference.
Imagine you have a class with getters that follow typical Java Bean naming convention or Java Record naming convention:
class Book {<br>...<br>public String getAuthor() { ... }<br>public String title() { ... }
How can we easily obtain "title" or "author" strings? Perhaps you want to directly parse CSV content, database results, or call a REST API:
String title = csvContents.get(nameOf(Book::title));<br>String author = dbQueryResults.get(beanNameOf(Book::getAuthor));<br>ListBook> books = bookClient.findByAttr(singletonMap(nameOf(Book::getAuthor), "Shakespeare"));
Solution #
We can implement such a nameOf(...) functionality in Java 8+ by serializing a method reference to SerializedLambda which lets us easily read the method’s name:
Declare a serializable getter functional interface: interface Getter extends Function, Serializable {}
When serializing it, Java will...