I'm working on legacy project and I've trapped into situation when I have to make additional init stuff with action object. In this code AdmAction is a basic interface and inside method I could have any of it's implementation. Some of implementations require additional properties must be initialized with values from utilParams.
private void initActionParams(AdmAction action, Map<String, Object> utilParams) {
if (utilParams == null) {
return;
}
utilParams.forEach((paramName, value) -> {
try {
Method setterMethod = action.getClass().getMethod(setterFor(paramName), value.getClass());
setterMethod.invoke(action, value);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error(e.getMessage(), e);
throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
}
});
}
utilParams looks like "serviceId": 10 of "ticketId": "8a30f5a7-809c-4551-8833-c2a60e4c6fd9".
Code works fine when value is an Object type (String, Integer etc.) and when setter method of AdmAction implementation consumes the same.
But there's one problem when I've got for example Integer type in utilParams and setter method in action which consumes int.
Of course code throws NoSuchMethodException
Example:
Action impl:
public class Foo implements AdmAction {
// ...
public void setServiceId(int serviceId) {
this.serviceId = serviceId;
}
}
Causes an exception.
I've tried to improve code with method search:
private void initActionParams(AdmAction action, Map<String, Object> utilParams) {
if (utilParams == null) {
return;
}
utilParams.forEach((paramName, value) -> {
try {
Method setterMethod = Arrays.stream(action.getClass().getDeclaredMethods())
.filter((Method method) -> method.getName().equals(setterFor(paramName)))
.findFirst()
.orElseThrow(NoSuchMethodException::new);
setterMethod.invoke(action, value);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error(e.getMessage(), e);
throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
}
});
I guess it's a little bit brute for actual case.
Can anybody help me find the way to write better and more aesthetic code?
You could use java.beans.Statement for this, which will do unboxing.
java.beans.Statement(action, setterFor(paramName), new Object[] {value})
.execute();
Related
i have a maps in java that contains generic value with key string:
String value1 = "value";
Long value2 = 100L;
Map<String, ?> items = new HashMap<>();
items.put("key1", value1);
items.put("key2", value2);
How can I execute a different method
public void customMethod(String str){}
or
public void customMethod(Long lng){}
with foreach map:
items.forEach((key, value) -> customMethod(value))?
thanks
Use reflection to dispatch the call to the appropriate method:
public void customMethod(Object obj) {
try {
getClass().getMethod("customMethod", obj.getClass()).invoke(this, obj);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
See live demo running with your examples.
However, this implementation is going to be slow because the method must be looked up every time it's used. To make this production ready, you can lookup the method once and cache and re-use it via its MethodHandle:
private static Map<Class<?>, MethodHandle> methodHandleCache = new HashMap<>();
public void customMethod(Object obj) {
try {
methodHandleCache.computeIfAbsent(obj.getClass(), this::findMethodHandle).invoke(this, obj);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
private MethodHandle findMethodHandle(Class<?> clazz) {
try {
MethodType methodType = MethodType.methodType(void.class, clazz);
return MethodHandles.lookup().findVirtual(getClass(), "customMethod", methodType);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
See live demo.
I have a Java program that calls an external API (RealApi in the code below) and sometimes I want to avoid calling this API and instead return pre-constructed responses (generated by FakeApi).
So, I ended up duplicating this kind of construct in most of my methods:
public Type1 m1(String s) {
try {
Type1 r = FakeApi.m1(s);
if (r != null) {
return r;
}
} catch (Exception e) {
// log error
}
return RealApi.m1(s);
}
What are some options to avoid duplicating this try/catch block everywhere? It's important that if FakeApi throws an exception or returns null, the RealApi must be called.
One option would be encapsulate the error checking behaviour into its own method:
public <T> T fakeOrReal(Supplier<T> fake, Supplier<T> real) {
try {
T r = fake.get();
if (r != null) {
return r;
}
}
catch (Exception e) {
// log error
}
return real.get();
}
You can then just call it with
public Type1 m1(String s) {
return fakeOrReal(() -> FakeApi.m1(s), () -> RealApi.m1(s));
}
This is not as simple as Thomas Preißler's answer but it will help you not repeat any method at all. So if you expand the interface, you have to modify only the concrete classes and not the linker which describes the actual behavior you want.
Create an interface that contains all the methods of RealApi:
interface Api {
Type1 m1(String s);
}
Then a class that does the actual call:
class ConcreteApi implements Api {
public Type1 m1(String s) {
return RealApi.m1(s);
}
}
Then create your FakeApi:
class TotallyFakeApi implements Api {
public Type1 m1(String s) {
return FakeApi.m1(s);
}
}
Now, the tricky part to avoid repeating yourself:
private static Object callImplementation(Api api, Method method, Object[] methodArgs) throws Exception {
Method actualMethod = api.getClass().getMethod(actualMethod.getName(), actualMethod.getParameterTypes());
return actualMethod.invoke(api, methodArgs);
}
Api fakeOrReal(Api fakeApi, Api realApi) {
return (Api) Proxy.newProxyInstance(
FakeApi.class.getClassLoader(),
new Class[]{Api.class},
(proxy, method, methodArgs) -> {
try {
Object r = callImplementation(fakeApi, method, methodArgs);
if (r != null) {
return r;
}
} catch (Exception e) {
// logError(e);
}
return callImplementation(realApi, method, methodArgs);
}
);
}
Get the actual implementation like this:
Api apiToUse = fakeOrReal(new TotallyFakeApi(), new ConcreteApi());
I have an Java POJO:
public class Event {
private String id;
private String name;
private Long time;
}
A simple filtering method I created is:
public static List<Event> simpleFilter(List<Event> eventList, String value) {
return eventList.stream().filter(Event -> Event.getName().equals(value)).collect(Collectors.toList());
}
Now my task is to create a generic method instead of simpleFilter which can be applied for any Java POJO object and any of its fields. For example, if in future there is a new Java object Employee and we want to filter on its String field employeeDepartment, we can use same generic filter method by passing the List of Java object (List<Employee>, Class type Employee.class, which field (getEmployeeDepartment) and what value ("Computer") we want to filter on.
I created a method definition:
public static <T> List<T> genericStringFilterOnList(List<T> list, Class<T> c, String methodName, String value) {
}
Caller looks like:
//events is List<Event>
//getName is the method in Event on which I want to filter
//e2 is value which I want to filter
genericStringFilterOnList(events, Event.class, "getName", "e2")
My implementation is:
public static <T> List<T> genericStringFilterOnList(List<T> list, Class<T> c, String methodName, String value) {
return list.stream().filter(m -> {
try {
return c.getMethod(methodName, null).invoke(c).equals(value);
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException e) {
} catch (InvocationTargetException e) {
} catch (NoSuchMethodException e) {
} catch (SecurityException e) {
}
return false;
}).collect(Collectors.toList());
}
All these catch were generated by IDE because of checked exception.
This doesn't seem to working because it is returning back an empty list.
What I am trying to do here is - Using the class type (which is Event.class), I am getting method name using reflection and then invoking that method and then invoke which is basically calling getName() method of Event class and then equals. I also tried this -
return c.getMethod(methodName, null).invoke(c.newInstance()).equals(value);
But with this I am getting NPE on this
}).collect(Collectors.toList());
Can you please help me in creating a generic method which can be called for a List of any POJO and a filter can be applied on any of its String type methods?
The invocation should happen on the steamed item, not on the class itself. The method Method::invoke(Object obj, Object... args) has two parameters:
obj - the object the underlying method is invoked from
args - the arguments used for the method call
Change the line:
return c.getMethod(methodName, null).invoke(c).equals(value);
.. to:
return c.getMethod(methodName, null).invoke(m).equals(value);
The confusion comes from the one-lettered variable names (see the solution below).
The whole solution shall be simplified. You don't want to extract the very same Method through reflection for the each object present in the stream pipeline. Extract the Method first and reuse it:
static <T> List<T> genericStringFilterOnList(List<T> list, Class<T> clazz, String method, String value) {
try {
// reflection invoked just once
Method method = clazz.getMethod(method, null);
// now streaming of the n items
return list.stream().filter(item -> {
try {
return value.equals(method.invoke(item));
} catch (IllegalAccessException | InvocationTargetException e) {}
return false;
}).collect(Collectors.toList());
} catch (NoSuchMethodException e) {}
return Collections.emptyList();
}
(Out of scope of this question): Note there is a higher probability that the invoked method returns null than the passed value is, therefore I'd go for value.equals(method.invoke(item)). Maybe, you want to add some additional comparison condition when both values are null to be compared.
Say I have the following code...
#FunctionalInterface
static interface MessageFunction<T> {
void send(T obj);
}
static #interface Message {
Class<?> value();
}
static class Foo {
#Message(String.class)
MessageFunction<String> bass = (string) -> {
// Do Stuff
};
}
static class MessageManager {
Map<Class<?>, MessageFunction<?>> messages = new HashMap<>();
public void register(Object obj) {
for (Field field : obj.getClass().getDeclaredFields()) {
Message message = field.getAnnotation(Message.class);
if (message != null) {
MessageFunction<?> function;
try {
function = (MessageFunction<?>) field.get(obj);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
return;
}
Method sendMethod;
try {
// Will this work?
sendMethod = function.getClass().getDeclaredMethod("send", Object.class);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
return;
}
// How do I do something like this?
/*if (sendMethod.testParamaters(message.value())) {
this.messages.put(message.value(), function);
}*/
}
}
}
}
public static void main(String[] args) {
MessageManager manager = new MessageManager();
manager.register(new Foo());
}
I am reflecting a field that references an #FunctionalInterface of a generic type. Because the method parameter is also generic I have no way of knowing what parameters it accepts, Thus I must pass it along through other means (the annotation).
The issue is that there is the annotation value and the generic type do not have to match and there seems to be no way to check. I wan't it to fail in registration if the type listed in the annotation would not be accepted into the send method.
How would I go about thing this without actually calling the method. Is there a way? Better yet although I know its most likely impossible, is there a way to know what the parameter type is without the annotation?
The following is just a suggestion, I have used it in my project. But it is not a perfect solution for the question. May be you can download the source of GenericHibernateDao framework and see the sourcecode of method "getTypeArguments". I think it is so cool!.
// get a class object for your entity
Class clazz = ...
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
Type trueType = ((ParameterizedType)type).getActualTypeArguments()[0];
Class modelClass = (Class) trueType;
// Now you can creat an Instance in you generic parameterType
Object entity = modelClass.forInstance();
}
I do something similar in some of my code Here is a snippet.
Method[] meths = actionClass.getMethods();
for (Method meth : meths) {
Class<?>[] pTypes = meth.getParameterTypes();
/*
* Filter out all methods that do not meet correct
* signature. The correct signature for an action method
* is: String actionName(HttpServletRequest request)
*/
//...check for the correct number of params and the correct param type
if (pTypes.length != 1 || !HttpServletRequest.class.toString().equals(pTypes[0].toString())) {
continue;
} else {
//...check for return type
if (!String.class.toString().equals(meth.getReturnType().toString())) {
continue;
}
}
//If you make it here than that means the method
//meets the requirements to be a full fledged action.
//...
}
All the examples I look at for reflection show creating a new instance of an unknown implementation, and casting that implementation to it's interface. The issue with this is that now you can't call any new methods (only overrides) on the implementing class, as your object reference variable has the interface type. Here is what I have:
Class c = null;
try {
c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
InterfaceType interfaceType = null;
try {
interfaceType = (InterfaceType)c.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
If I only have a reference to "com.path.to.ImplementationType", and I don't know what that type might be (it is coming from a config file), then how can I use the class name to cast it to ImplementationType? Is this even possible?
This line seems to sum up the crux of your problem:
The issue with this is that now you can't call any new methods (only overrides) on the implementing class, as your object reference variable has the interface type.
You are pretty stuck in your current implementation, as not only do you have to attempt a cast, you also need the definition of the method(s) that you want to call on this subclass. I see two options:
1. As stated elsewhere, you cannot use the String representation of the Class name to cast your reflected instance to a known type. You can, however, use a String equals() test to determine whether your class is of the type that you want, and then perform a hard-coded cast:
try {
String className = "com.path.to.ImplementationType";// really passed in from config
Class c = Class.forName(className);
InterfaceType interfaceType = (InterfaceType)c.newInstance();
if (className.equals("com.path.to.ImplementationType") {
((ImplementationType)interfaceType).doSomethingOnlyICanDo();
}
} catch (Exception e) {
e.printStackTrace();
}
This looks pretty ugly, and it ruins the nice config-driven process that you have. I dont suggest you do this, it is just an example.
2. Another option you have is to extend your reflection from just Class/Object creation to include Method reflection. If you can create the Class from a String passed in from a config file, you can also pass in a method name from that config file and, via reflection, get an instance of the Method itself from your Class object. You can then call invoke(http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object, java.lang.Object...)) on the Method, passing in the instance of your class that you created. I think this will help you get what you are after.
Here is some code to serve as an example. Note that I have taken the liberty of hard coding the params for the methods. You could specify them in a config as well, and would need to reflect on their class names to define their Class obejcts and instances.
public class Foo {
public void printAMessage() {
System.out.println(toString()+":a message");
}
public void printAnotherMessage(String theString) {
System.out.println(toString()+":another message:" + theString);
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("Foo");
Method method1 = c.getDeclaredMethod("printAMessage", new Class[]{});
Method method2 = c.getDeclaredMethod("printAnotherMessage", new Class[]{String.class});
Object o = c.newInstance();
System.out.println("this is my instance:" + o.toString());
method1.invoke(o);
method2.invoke(o, "this is my message, from a config file, of course");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException nsme){
nsme.printStackTrace();
} catch (IllegalAccessException iae) {
iae.printStackTrace();
} catch (InstantiationException ie) {
ie.printStackTrace();
} catch (InvocationTargetException ite) {
ite.printStackTrace();
}
}
}
and my output:
this is my instance:Foo#e0cf70
Foo#e0cf70:a message
Foo#e0cf70:another message:this is my message, from a config file, of course
//====Single Class Reference used to retrieve object for fields and initial values. Performance enhancing only====
Class<?> reference = vector.get(0).getClass();
Object obj = reference.newInstance();
Field[] objFields = obj.getClass().getFields();
I'm not absolutely sure I got your question correctly, but it seems you want something like this:
Class c = null;
try {
c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
T interfaceType = null;
try {
interfaceType = (T) c.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Where T can be defined in method level or in class level, i.e. <T extends InterfaceType>
As an addendum to akf's answer you could use instanceof checks instead of String equals() calls:
String cname="com.some.vendor.Impl";
try {
Class c=this.getClass().getClassLoader().loadClass(cname);
Object o= c.newInstance();
if(o instanceof Spam) {
Spam spam=(Spam) o;
process(spam);
}
else if(o instanceof Ham) {
Ham ham = (Ham) o;
process(ham);
}
/* etcetera */
}
catch(SecurityException se) {
System.err.printf("Someone trying to game the system?%nOr a rename is in order because this JVM doesn't feel comfortable with: “%s”", cname);
se.printStackTrace();
}
catch(LinkageError le) {
System.err.printf("Seems like a bad class to this JVM: “%s”.", cname);
le.printStackTrace();
}
catch(RuntimeException re) {
// runtime exceptions I might have forgotten. Classloaders are wont to produce those.
re.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
}
Note the liberal hardcoding of some values. Anyways the main points are:
Use instanceof rather than equals(). If anything, it will co-operate better when refactoring.
Be sure to catch these runtime errors and security ones too.
You want to be able to pass in a Class and get a type-safe instance of that class? Try the following:
public static void main(String [] args) throws Exception {
String s = instanceOf(String.class);
}
public static <T> T instanceOf (Class<T> clazz) throws Exception {
return clazz.newInstance();
}
If you knew the Class of ImplementationType you could create an instance of it. So what you are trying to do is not possible.