Last active
December 25, 2015 00:49
-
-
Save ChristinGorman/6890462 to your computer and use it in GitHub Desktop.
Attempt at function pointers in java :-)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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