Skip to content

Instantly share code, notes, and snippets.

@ChristinGorman
Last active December 25, 2015 00:49
Show Gist options
  • Save ChristinGorman/6890462 to your computer and use it in GitHub Desktop.
Save ChristinGorman/6890462 to your computer and use it in GitHub Desktop.
Attempt at function pointers in java :-)
package no.gorman.stop.it;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
/**
* I like using Mockito in tests and wondered if I could create
* a similar way of getting at individual methods/functions in production code.
*
* Say you have the following class:
* public class Test {
* private final String myVariable;
*
* public Test(String myVariable) {
* this.myVariable = myVariable;
* }
*
* public void doSomething(){}
* public String getText() { return myVariable;}
* }
*
* If you want to reference the getText() method directly without using reflection,
* you could use the FuncPtr class like this (I like using static imports to reduce line length)
* Test a = new Test("Hello world");
* Func<String> strFunc = ptr(record(a).getText());
* System.out.println(strFunc.call()); //prints "Hello world"
*
* Alternatively you cann pass in the class:
* Func<String> strFunc = ptr(recordClass(Test.class).getText());
*
* But then you'd have to pass in which instance you want to call the method on:
* System.out.println(strFun.callOn(new Test("Hello world"))); //still prints "Hello world"
*
* For void methods it's a bit worse, you need two lines:
* Test a = new Test("Hello world");
* record(a).doSomething();
* Func voidMethod = FuncPtr.getFunction();
*
* Not ideal, but oh well.
*
* I've actually used this approach a few times now and found it useful,
* so I thought I'd share.
*
*/
public class FuncPtr {
static ThreadLocal<Func<?>> lastCalled = new ThreadLocal<>();
public static <T> T recordClass(final Class<T> clazz) {
return record(null, clazz);
}
@SuppressWarnings("unchecked")
public static <T> T record(final T obj) {
return record(obj, (Class<T>) obj.getClass());
}
@SuppressWarnings("unchecked")
public static <T> Func<T> ptr(T returType) {
return (Func<T>) lastCalled.get();
}
public static Func<?> getFunction() {
return lastCalled.get();
}
@SuppressWarnings("unchecked")
public static <T> T record(final T instance, final Class<T> clazz) {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(clazz);
try {
return (T) factory.create(null, null, new MethodHandler() {
@Override
public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args) throws Throwable {
lastCalled.set(Func.withReturnTypeSet(thisMethod.getReturnType(), instance, thisMethod));
return null;
}
});
} catch (NoSuchMethodException | IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static class Func<T> {
private final Method method;
private final Object instance;
private Func(Class<T> returtype, Object instance, Method m) {
this.instance = instance;
this.method = m;
}
public T call(Object... args) {
return callOn(instance, args);
}
public Method getMethod() {
return method;
}
public static <T> Func<T> withReturnTypeSet(Class<T> clazz, Object instance, Method m) {
return new Func<T>(clazz, instance, m);
}
@SuppressWarnings("unchecked")
public T callOn(Object obj, Object... args) {
try {
method.setAccessible(true);
return (T)method.invoke(obj, args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment