JAVA Reflection invoke method with casting argument failed - java

I want to call method of a reflection of okhttp3.Request. The method to be called is writeTo(okio.BufferedSink).
private String getBody(final Object body){
try {
final BufferedSink buffer = Okio.buffer((Sink) new Buffer());
Method methodWriteTo = findMethod(body.getClass(), "writeTo");
# Not working
methodWriteTo.invoke(body, BufferedSink.class.cast(buffer));
# Not working
methodWriteTo.invoke(body, (BufferedSink)buffer);
String body = buffer.buffer().readUtf8();
return body;
} catch (Exception e) {
...
}
return "";
}
I always got http3.RequestBody$Companion$toRequestBody$1.writeTo argument 1 has type okio.BufferedSink, got okio.RealBufferedSink.
The okio.BufferedSink is interface class, so how can invoke this method with okio.RealBufferedSink ?

Related

Java reflection inside stream's filter

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.

Get Method reference from Method object

I have a method that expects a method reference:
expectsMethodRef(obj::someMethod);
I now only retrieve the method at compiletime with reflection. How do I get the method reference from a Method object?
Method method = obj.class.getMethod(methodNameStr);
expectsMethodRef(<how to input my method here?>);
You just need to use Method.invoke. Here is an example:
public class SomeObject{
public String someMethod(){
return "Test";
}
}
public String expectsMethodRef( Function<SomeObject, String> f ){
SomeObject so = new SomeObject();
return f.apply(so);
}
And here is how you invoke using plain lambda and Method object.
//plain lmbda
expectsMethodRef( SomeObject::someMethod );
//with method object
Method someMethod = SomeObject.class.getMethod("someMethod");
expectsMethodRef( (so) -> {
try {
return (String)someMethod.invoke(so);
} catch (Exception e) {
return null;
}
} );

Passing the generic type dynamically into the method to build a instance of an object using that type

Can someone please tell me whether there is a way to pass the generic type T to getAPIResponse(...) and build the TypeReference within getAPIResponse method.
I would like to pass Model as a generic type to getAPIResponse and have TypeReference built in within the getAPIResponse method.
TypeReference<APIResponse<Model>> type = new TypeReference<APIResponse<Model>>(){};
APIResponse<Model> response = getAPIResponse(result, type);
I would like to avoid building the TypeReference instance outside the getAPIResponse method, rather would like to create the instance within the getAPIResponse method and pass only the Generic type into the method.
getAPIResponse method
protected final <T> APIResponse<T> getAPIResponse(MvcResult pResult, TypeReference<APIResponse<T>> type) {
APIResponse<T> res = null;
try {
res = new ObjectMapper().readValue(pResult.getResponse().getContentAsString(), type);
}
catch (IOException e) {
Assert.fail(ExceptionUtils.getStackTrace(e));
}
return res;
}
I tried changing to the below
protected final <T> APIResponse<T> getAPIResponse(MvcResult pResult, T t) {
APIResponse<T> res = null;
try {
TypeReference<APIResponse<T>> type = new TypeReference<APIResponse<T>>(){};
res = new ObjectMapper().readValue(pResult.getResponse().getContentAsString(), type);
}
catch (IOException e) {
Assert.fail(ExceptionUtils.getStackTrace(e));
}
return res;
}
But not I am not sure how to call into this method. Lets say I want to return
APIResponse<Bank>
APIResponse<Branch>
APIResponse<List<Employee>>
How do I pass these generic types into my method call in the invoking class?
UPDATE: It doesn't work. JSON is converted to Map instead of APIResponse, .... :(
Try this:
protected final <T> APIResponse<T> getAPIResponse(MvcResult pResult) {
APIResponse<T> res = null;
try {
TypeReference<APIResponse<T>> type = new TypeReference<APIResponse<T>>(){};
res = new ObjectMapper().readValue(pResult.getResponse().getContentAsString(), type);
}
catch (IOException e) {
Assert.fail(ExceptionUtils.getStackTrace(e));
}
return res;
}
And call:
APIResponse<Model> response = getAPIResponse(result);
You don't need give 't' object.

Java Method.invoke() throwing IllegalArgumentException: argument type mismatch

I am trying to invoke a static method with a Object[] parameter type. When I debug, the correct method is identified and the parameter type I put in seems to me to be of the correct type.
public String convertToJSFunction(Method method, Object[] params) {
String function = method.getName();
for (Method m : JavaToJS.class.getDeclaredMethods()) {
if (m.getName().equals(function))
try {
return (String) m.invoke(null,params);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (InvocationTargetException e) {
e.printStackTrace();
return null;
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
}
}
return null;
}
JavaToJS has only static methods. After debugging, the m I am trying to invoke is this method:
public static String setRegionNumber(Object[] params)
This throws an IllegalArgumentException: argument type mismatch. How is this possible?
i guess you are calling
Method setRegionNumber=...; // "setRegionNumber" Method
Object[] params=...; // your Object-Array Parameter
convertToJSFunction(setRegionNumber, params);
but what you need to do is
Method setRegionNumber=...; // "setRegionNumber" Method
Object[] params=...; // your Object-Array Parameter
convertToJSFunction(setRegionNumber, new Object[] { params });
this is because Method.invoke expects the parameter list of the called method as an object Array. So if you pass your object array directly then it interprets that as the parameter list. so if you have an Object[] Parameter you need to wrap it in an Object-Array just like any other parameter.

Mirroring a method with string parameter

I am trying to use mirroring to call a method s1 of my class MyClass, with the parameter s2. Java is complaining that String.TYPE does not exist. I checked the API and it is right: I cannot do the same thing as when I call Integer.TYPE. But how can I solve the problem then? I need partype of type String, or else the method throws an exception.
public void trying(MyClass method, String s1, String s2){
try {
Class cls = Class.forName("MyClass");
Class partype[] = new Class[1];
partype[0] = String.TYPE;
Method meth = cls.getMethod(s1, partype);
meth.invoke(methobj, s2);
}
catch (Throwable e) {
System.err.println(e);
}
}
It's not a type, it's a class:
partype[0] = String.class;

Categories