Hacking the Method Name in Java

theanonymousone2 pts0 comments

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...

method java public string nameof name

Related Articles