Using Java Reflection In a DSL-like
Style
I know someone must have heard or known about a library named FEST-Reflect,
and maybe you are just using it. It's an interesting library that let you write
code of java reflection in a DSL-like style. For example:
String name = method("get").withReturnType(String.class)
.withParameterTypes(int.class)
.in(names)
.invoke(8);
It's just another way to express same logic, but it give you another way to
write your code which make your code more readable. If you want to write your
code this way, of course, you can use FEST-reflect directly, but that's not why
wrote words below, what I try to tell you is, you can implement such things
yourself with little effort. I will draft a prototype to implement such a
DSL-like style Java Reflection usage API, If you are interested, read on...
To enable the users to use our API in a DSL-like style like code sample
above, it's easy to figure out 2 things:
-
We have to enable method chaining.
-
Static import feature after Java5 may be needed so that the code looks like a
DSL syntax.
So first, we have to find out which operations or methods can be used.
We focus on only java reflection usage on Instance Method, since it's not a
difficult thing to find out what we can do with Method, An abstraction can be
given below:
/**
* method("name").on(targetobject).withArgumentTypes(...).invoke(...);
*
* @author fujohnwang
* @since 1.0
*/
public interface IMethodDSL {
IMethodDSL on(Object target);
IMethodDSL withArgumentTypes(Class<?>... args);
IMethodDSL makeAccessible(boolean flag);
<T> T invoke(Object... args) throws InvocationTargetException;
}
We express the abstraction in an Java interface, except for the last “invoke
” method, other operations have a return type of the
same interface, that's, IMethodDSL itself. That's the way we implement a method
chaining behavior.
Since we have had the key abstraction, we can give it an implementation now,
it looks like below:
/**
* Default IMethodDSL implementation.<br>
*
* @author fujohnwang
* @since 1.0
* @see ReflectDSL
* @see IMethodDSL
*/
public class MethodDSL implements IMethodDSL {
private static final Logger logger = LoggerFactory.getLogger(MethodDSL.class);
private String methodName;
private Object target;
private Class<?>[] argTypes;
private boolean accessible;
public MethodDSL(String methodName) {
this.methodName = methodName;
}
public <T> T invoke(Object... args) throws InvocationTargetException {
checkInvokeDependencies();
try {
Method method = findQualifiedMethod(target.getClass());
if(this.accessible)
{
method.setAccessible(true);
}
/**
* usually, the caller will be required to declare proper return type of the method invocation,
* so cast to the type they declared is acceptable.
* the return type the caller declared should be the returnType they have assigned.
*/
@SuppressWarnings("unchecked")
T result = (T)method.invoke(target, PreConditions.nullAsEmpty(args,Object.class));
return result;
} catch (SecurityException e) {
throw new InvocationTargetException(e);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException(e);
} catch (IllegalArgumentException e) {
throw new InvocationTargetException(e);
} catch (IllegalAccessException e) {
throw new InvocationTargetException(e);
}
}
protected Method findQualifiedMethod(Class<?> clazz) throws SecurityException, NoSuchMethodException {
if(this.argTypes == null)
{
Method qualifiedMethod = null;
for(Method method : clazz.getDeclaredMethods())
{
if(method.getName().equals(this.methodName))
{
if(qualifiedMethod != null)
{
throw new IllegalStateException("please provide arguments of method if you want to invoke on overloaded methods.");
}
qualifiedMethod = method;
}
}
if(qualifiedMethod == null)
{
throw new NoSuchMethodException();
}
return qualifiedMethod;
}
else
{
return clazz.getDeclaredMethod(this.methodName, this.argTypes);
}
}
/**
* usually, these information like "methodName", "target" are required, <br>
* but in case special situations, this method is declared protected so that in those situations,
* others can override this default behavior.
*/
protected void checkInvokeDependencies() {
logger.info("the data is lazily bound before the real invocation of the method, check before invocation here.");
PreConditions.isNotEmpty(methodName);
PreConditions.isNotNull(target);
}
public IMethodDSL on(Object target) {
this.target = target;
return this;
}
public IMethodDSL withArgumentTypes(Class<?>... args) {
this.argTypes = args;
return this;
}
public IMethodDSL makeAccessible(boolean flag) {
this.accessible = flag;
return this;
}
}
The intermediate operations just accept the parameter value and “return this;
” to achieve method chaining, the key point is
the last operation, that's, the “invoke
” method, this
is where all of the real stuff take effect. We check the preconditions before
invoking Java Reflection API, and then find proper Method with attribute the API
user have passed in as chaining-method's parameter. At last, cast the invocation
result to the type the users expect to get. That's all, very simple , isn't it?
To make API users to use this more DSL-likely, we need to offer a
Factory-method for our MethodDSL, it looks like:
public abstract class ReflectDSL {
public static IMethodDSL method(String methodName)
{
return new MethodDSL(methodName);
}
...
}
With this, we can use our DSL-like Java Reflection API this way:
import static ..ReflectionDSL.*;
method("methodName")
.on(targetObject)
.withArgumentTypes(...)
.makeAccessable(true)
.invoke(...);
Of course, some parameters are optional, like the makeAccessable() if it's a
public method.
Until now, we have implement a simple DSL-like style java reflection AP for
method, you can move on to provide such similar APIs for Field and Constructor
and Static Method and so on.
The way we used to implement such DSL-like style API have some limitations or
defects, for example, we bind intermediate method late and the final effect
takes at last, that means, if users use our API in an improper way, they will
not get warnings or errors until runtime. To fix this, we can use intermediate
type for chaining methods, how? try it yourself and have fun ;-)
|
Note
IMHO, this may not bring any benefits for you or your customers, it's just
another way to write your code and make it more readable, make a balance to
figure out whether it's proper to do it in your own situations.
|
相关推荐
Java Reflection in Action is unique in presenting a clear account of all the cool things you can do with reflection, and at the same time pro- viding the sound conceptual basis that developers need to...
Java Reflection in Action is unique in presenting a clear account of all the cool things you can do with reflection, and at the same time providing the sound conceptual basis that developers need to ...
"Computer Graphics Using Java 2D and 3D"这本书可能涵盖这些主题,提供源代码和习题解答,帮助读者深入理解理论并实践编程技巧。 通过阅读这本书,你将学到如何使用Java 2D和3D API来构建图形用户界面,创建动态...
Detecting potential hucksterism in meta-analysis using a follow-up fail-safe test fSyrhO/Ogy in the Schools Volume 29. April 1992 DETECTING POTENTIAL HUCKSTERISM IN META-ANALY SIS USING A FOLLOW...
Needle in A Haystack -- Catch Multiple Zero-days Using Sandbox
AI复现大脑导航功能:DeepMind重大研究突破再次登上Nature,今天,DeepMind 在《Nature》上新发表的一篇论文引起了业内极大的关注,他们使用深度学习技术来训练一只老鼠,在虚拟环境中追踪其位置,模拟人类大脑的空间...
第四章 面向对象编程--Object Oriented Concepts using Java 第五章 接口--Interfaces in Java 第六章 包--Concept of Packages 第七章 异常--Exception Handling in Java 第二部分包括: IO Streams in Java--IO...
plus Java developers with a basic all-in-one programming reference * Covers the recent release of the Java 2 Platform Standard Edition 5.0 and the new J2SE Development Kit 5.0 * Starts with ...
Simulating planar reflection using two-pass rendering and texture mapping 1. There ‘s a table with flat rectangular semi-reflective table-top in the 3D scene 2. On/Around the table are some 3D ...
Build a Twitter-like web application called Bullhorn using Java, Oracle, and more Create web applications using Eclipse Design web pages with HTML forms, tables, and more Use SQL along with Java ...
Welcome to the fourth edition ofObject-Oriented Data Structures Using Java™. This book pres- entsthealgorithmic,programming,andstructuringtechniquesofatraditionaldatastructures course in an object-...
最新出版的Digital Image Processing-An Algorithmic Introduction Using Java-2nd,书很精致,对图像处理的细节解释的非常浅显易懂,不可多得的一本好书,强烈推荐阅读!
【标题】"SOA Using Java in Webservice" 指的是使用Java技术在Web服务中实现面向服务架构(Service-Oriented Architecture, SOA)的一种方法。SOA是一种设计原则,它强调通过独立、可重用的服务来构建分布式系统,...
Title: Problem Solving in Data Structures & Algorithms Using Java: The Ultimate Guide to Programming Author: Hemant Jain Length: 436 pages Edition: First Edition Language: English Publisher: ...
This updated edition of Java in a Nutshell not only helps experienced Java programmers get the most out of Java versions 9 through 11, it’s also a learning path for new developers. Chock full of ...