-
-
Save josefbetancourt/3ffcb3044e558fc1b3e8 to your computer and use it in GitHub Desktop.
Here is an example of creating a fluent builder interface for invoking a method via Java Reflection. While the code to invoke Java methods via reflection is not complex, it can be improved. We can introduce a ‘façade’ and this façade can use the fluent builder pattern. One example of using this is: .name(“greet”).on(anObject).with(“Hello world!”…
This file contains 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 com.octodecillion.utils; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.security.AccessController; | |
import java.security.PrivilegedExceptionAction; | |
/** | |
* Reflection Invoker using Fluent Builder. | |
* | |
* Example is a singleton utility class that contains | |
* one utility that is used in a fluent builder pattern. | |
* | |
* @author jbetancourt | |
* @since 20130201T2233-5 | |
* | |
*/ | |
public final class ReflectionUtil { | |
private static final ReflectionUtil instance = new ReflectionUtil(); | |
private ReflectionUtil() { | |
// it's a singleton utility class. | |
} | |
public static Invoker invokes() { | |
return instance.new Invoker(); | |
} | |
/** | |
* Example use: | |
* invokes().name("hello").on(innerTest).using("Hello world!").of(String.class).invoke(); | |
* | |
*/ | |
class Invoker { | |
private String name; | |
private Object object; | |
private Class<? extends Object> clazz; | |
private Class<?>[] classes; | |
private Object[] params; | |
private Method method; | |
private boolean allowPrivate; | |
public Invoker name(final String name) { | |
this.name = name; | |
return this; | |
} | |
public Invoker on(final Object obj) { | |
object = obj; | |
return this; | |
} | |
public Invoker on(final Class<?> clz) { | |
clazz = clz; | |
return this; | |
} | |
public Invoker of(final Class<?>... classes) { | |
this.classes = classes; | |
return this; | |
} | |
public Invoker using(final Object... params) { | |
this.params = params; | |
return this; | |
} | |
public Invoker nosy() { | |
allowPrivate = true; | |
return this; | |
} | |
public Invoker method(final Method method) { | |
this.method = method; | |
return this; | |
} | |
public <T> Invoker returning(@SuppressWarnings("unused") final T clazz) { | |
return this; | |
} | |
public Object invoke() throws NoSuchMethodException, SecurityException, | |
IllegalAccessException, IllegalArgumentException, | |
InvocationTargetException { | |
boolean paramsButNoTypes = (method == null) | |
&& ((params != null) && (classes == null)); | |
if (paramsButNoTypes) { | |
throw new IllegalStateException(String.format( | |
"params:%s,classes:%s", params, classes)); | |
} | |
if (method == null) { | |
if (object == null) { | |
method = clazz.getDeclaredMethod(name, classes); | |
} else { | |
Class<? extends Object> clz = (clazz == null ? object | |
.getClass() : clazz); | |
method = clz.getDeclaredMethod(name, classes); | |
} | |
} | |
if (allowPrivate) { | |
return invokePrivate(); | |
} | |
return method.invoke(object, params); | |
} | |
private Object invokePrivate() { | |
try { | |
final Object objF = object; | |
final Method methodF = method; | |
final Object[] paramsF = params; | |
return AccessController | |
.doPrivileged(new PrivilegedExceptionAction<Object>() { | |
Object result; | |
@Override | |
public Object run() throws Exception { | |
if (!methodF.isAccessible()) { | |
methodF.setAccessible(true); | |
} | |
result = methodF.invoke(objF, paramsF); | |
return result; | |
} | |
}); | |
} catch (Exception ex) { | |
throw new IllegalStateException("Cannot set method,'" + method | |
+ "' accessible.", ex); | |
} | |
} | |
} // end class Invoker | |
} // end class Example |
This file contains 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 com.octodecillion.utils; | |
import static org.hamcrest.core.Is.is; | |
import static org.junit.Assert.assertThat; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.junit.runners.JUnit4; | |
/** | |
* | |
* | |
* @author jbetancourt | |
* | |
*/ | |
@RunWith(JUnit4.class) | |
public class ReflectionUtilTest { | |
/** test on class with static public methods */ | |
@Test | |
public final void invokes_on_static() throws Exception { | |
Object actual = ReflectionUtil.invokes().name("hello") | |
.on(WithStaticMethod.class).using("Hello world!") | |
.of(String.class).invoke(); | |
assertThat((String) actual, is("Hello world!")); | |
} | |
/** test on class with static private methods */ | |
@Test | |
public final void invokes_on_private_method() throws Exception { | |
WithStaticMethod obj = new WithStaticMethod(); | |
Object actual = ReflectionUtil.invokes().name("privateHello").on(obj).nosy() | |
.invoke(); | |
assertThat((String) actual, is("Hello private world!")); | |
} | |
static class WithStaticMethod { | |
public static String hello(final String msg) { | |
return msg; | |
} | |
private String privateHello() { | |
return "Hello private world!"; | |
} | |
} | |
/** test on inner class with public methods */ | |
@Test | |
public final void testInvokes() throws Exception { | |
class InnerClass { | |
public String hello(final String msg) { | |
return msg; | |
} | |
public String hello() { | |
return "Hello default world!"; | |
} | |
private String privateHello() { | |
return "Hello private world!"; | |
} | |
} // end InnerTest | |
InnerClass innerTest = new InnerClass(); | |
Object actual = ReflectionUtil.invokes().name("hello").on(innerTest) | |
.using("Hello world!").of(String.class).invoke(); | |
assertThat((String) actual, is("Hello world!")); | |
actual = ReflectionUtil.invokes().name("hello").on(innerTest).invoke(); | |
assertThat((String) actual, is("Hello default world!")); | |
actual = ReflectionUtil.invokes().name("privateHello").on(innerTest).nosy() | |
.invoke(); | |
assertThat((String) actual, is("Hello private world!")); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment