Problem with Method.invoke - java

I have a method that return a list of objects and I want to call it by the invoke method of the class Method. The only problem is that invoke method returns an Object and not a list<Object>.
The code is here:
Class<? extends AnObject> anObject = MyObject.getClass();
Method myMethod = MyObject.getMethod("getListObject");
Object objject = method.invoke(MyObject); // I want it to return list
How can I solve it?

Class<? extends AnObject> anObject = MyObject.getClass();
Method myMethod = MyObject.getMethod("getListObject");
List object = (List)myMethod.invoke(anObject);

Just typecast it:
List list = (List)method.invoke(...);

You can explicitly cast the result to the type you desire.
List<?> theList = (List<?>) method.invoke(anObject, new Object[] {})
This may result in a ClassCastException in runtime if the method doesn't return the type you expected.

How about letting dp4j's annotations processor figure it out?
#com.dp4j.Reflect //or #org.junit.Test
public void test(){
Object objject = MyObject.getListObject();
}
You can also print the injected code with -Averbose=true.

what let me to this page is
when I tried to invoke a protected method without making it Accessible
take look at my now working version
DoPubService service = SpringAdapter.getBean("frontendDoPubService", DoPubService.class);
try {
Method runUrlReplacerMethod = service.getClass().getDeclaredMethod("runUrlReplacer", String.class, String.class, String.class);
return runUrlReplacerMethod.invoke(service, "10.21019/qna-900031", "abs", explanation);
} catch (Exception e) {
e.printStackTrace();
}
this is not working because the Method should be Accessible before invoking it
runUrlReplacerMethod.setAccessible(true);

Java 8 solution:
List<?> list = (List) invokeResult;
List<String> = list.stream().map(el -> (String) el).collect(Collectors.toList());
Change String to type you need.

Related

how to pass a `Class<T>` of `List<MyDevice>`?

I wrote this method:
public <T> T jsonToObject(String json, Class<T> klazz) {
ObjectMapper mapper = new ObjectMapper();
T object = null;
try {
object = mapper.readValue(json, klazz);
} catch (IOException e) {
e.printStackTrace();
}
return object;
}
I want to call it:
List<Device> devicesList= jsonUtils.jsonToObject(response.getEntityInputStream(), new TypeLiteral<List<Device>.class);
what is the correct way to pass a Class<T> of List<Device> ?
Well, if you want to call it with a TypeLiteral (and that's indeed the way to capture the generic type of the list), your method needs to accept a TypeLiteral<T> as argument, not a Class<T>.
And the caller needs to use an anonymous class to properly capture the type:
jsonUtils.jsonToObject(response.getEntityInputStream(),
new TypeLiteral<List<Device>>() {});

Generics: ClassCastException: java.lang.Object[] cannot be cast to

I I have a simple class that uses generics.
public class ResponseWorkerRunnable<Parameter, Result> implements Runnable {
private final ResponseWorker<Parameter, Result> worker;
/**
* The parameters for {#link ResponseWorker#doInBackground(Object...)}
*/
private final Parameter[] params;
public ResponseWorkerRunnable(ResponseWorker<Parameter, Result> worker,
Parameter... params) {
uiThreadHandler = new Handler(Looper.getMainLooper());
this.worker = worker;
this.params = params;
}
#Override
public void run() {
try {
Result res = worker.doInBackground(params);
postResultBack(res);
} catch (Exception e) {
postErrorBack(e);
}
}
}
and my ResponseWorker:
public interface ResponseWorker<Parameter, Result> {
public Result doInBackground(Parameter... param) throws Exception;
}
The problem is, that I get ClassCastException:
java.lang.ClassCastException: java.lang.Object[] cannot be cast to
model.Table[]
I do something like this:
Table t = new Table();
ResponseWorker<Table, SuperTable> worker = ... ;
ResponseWorkerRunnable<Table, SuperTable> r = new ResponseWorkerRunnable<Table, SuperTable>(worker, t);
Than the ResponseWorkerRunnable will be scheduled and will run in the future with this exception:
java.lang.ClassCastException: java.lang.Object[] cannot be cast to
model.Table[]
at this line in the ResponseWorkerRunnable run() method:
Result res = worker.doInBackground(params);
I have used the debugger to check the Parameter[] params field (in ResponseWorkerRunnable) and its set to Object[1]{Table#4237c0e0}
So its an array of object but ResponseWorker.doInBackground expects an Array of class Table.
How do I cast it correctly?
Result res = worker.doInBackground((Parameter[]) params);
Any other idea or hint what could be wrong?
------ UPDATE -------
I use a singleton class called ResponseWorkerExecutor schedule the ResponseWorkerRunnable (with a ThreadPool) to
class ResponseWorkerExecutor {
public static <Parameter, Result> Future<?> submit(
ResponseWorker<Parameter, Result> responseWorker, Parameter ... params) {
return INSTANCE.executor
.submit(new ResponseWorkerRunnable<Parameter, Result>(
responseWorker, params));
}
}
So in my code I do something like this:
I do something like this:
Table t = new Table();
// ResponseWorker implementation
ResponseWorker<Table, SuperTable> worker = ... ;
// Here is the problem with the args
ResponseWorkerExecutor.submit(worker, t);
Due to the way generics work in Java (read here about type erasure) the actual Parameter class is being replaced by Object in the resulting bytecode, this is why your varargs array is Object[] and not Table[].
There is a workaround in this case that should work, it involves some changes to your code:
// Pass the actual class object to your Runnable, in this case t.getClass() -> Table.class
ResponseWorkerRunnable<Table, SuperTable> r = new ResponseWorkerRunnable<Table, SuperTable>(worker, t, t.getClass());
And then:
public class ResponseWorkerRunnable<Parameter, Result> implements Runnable {
private final ResponseWorker<Parameter, Result> worker;
/**
* The parameters for {#link ResponseWorker#doInBackground(Object...)}
*/
private final Parameter[] params;
private final Class<?> actualClass;
public ResponseWorkerRunnable(ResponseWorker<Parameter, Result> worker, Parameter... params, Class<?> actualClass) {
uiThreadHandler = new Handler(Looper.getMainLooper());
this.worker = worker;
this.params = params;
this.actualClass = actualClass;
}
#Override
public void run() {
try {
Result res = worker.doInBackground((Parameter[]) Arrays.copyOf(params, params.length, actualClass));
postResultBack(res);
} catch (Exception e) {
postErrorBack(e);
}
}
}
What this does is take the Object[] and copying its contents into a new, real Parameter[], whatever the actual class Parameter refers to. Then it makes the varargs call using this new array.
Using "Parameters" instead of the conventional "P" makes your code harder to read. What is happening is this. The type of this.params is correctly set to Parameter[]. If you passed a value to the constructor, then it would also be checked against Parameter[]. However, you didn't pass an argument, so the runtime creates an empty array for you. Unfortunately, it isn't smart enough to recognize the now erased type Parameter, so it creates an Object[]. I don't know if it should or not, but it isn't.
I understand what you are doing, and it makes sense. One way to "fix" the problem inside the constructor is to check the type of "params". Given that it is an array, you may not be able to use instanceof. Or, you can simply check to see if it empty. If you didn't receive a Parameter[], ignore "params" and create a new, emtpy Parameter[] and assign it to "this.params".
Use, that fixes it (at least thats what my Eclipse said ;-) )
public Result doInBackground(Parameter[] param);
If that fixed it, there seems to be a problem with the varags declaration and generics.
So I have sold this problem with a workaround. I use List instead of Parameter[] or Parameter ... params .
There are already some help methods in java.util.Collections class like: Collections.singletonList(param);
So in my case that seems to me the best solution, since I have only a single line of code, where I have to put a single object in a List<> or to convert a array to a list. Hence this method is part of a little library the user of the library does not have to take care about it.
The solution with Arrays.copyOf(params, params.length, actualClass)); suggested by #gpeche needs an aditional Class as parameter, and at the end the user of the library have to add the class.
So I guess I found a compromise by using List instead of Parameter ... params

Obtaining types through parameters on Java

Certainly I'm quite new in all this Java stuff, so I have a question, I'm trying to deserialize a response obtained on a WCF service, everything works fine, but, I'm trying to make a generic function to do this.
Basically what I do is
public List<msg> GetService(String method){
List<msg> response = new ArrayList<msg>();
Type msgType = new TypeToken<List<msg>>(){}.getType();
//Obtaining result
response = uJSON.fromJson(serviceResponse, msgType);
//uJSON is an instance of Gson library, for deserializing it just needs
//the service response and a Class<T> or Type to reflect the obtained message
}
What I'm trying to do is obtaining the Type "msg" generic, it means...
public <thing> void GetInstanceService(String method){
List<thing> response = new ArrayList<thing>();
Type rType2 = new TypeToken<List<thing>>(){}.getType(); //Got java.util.List<thing>
//And when I'm trying to deserialize I just obtain a List of object
//[java.lang.Object#5c7a987e, java.lang.Object#74b1a7a0]
type2 = uJSON.fromJson(new String(entity), rType2);
}
But I'm calling like this.
comm.<msgType>GetInstanceService("listTestType");
So, when I call "GetInstanceService", "thing" is "msgType" Type, for the
List<thing> and also response shouldn't be List<msgType> instead of List <Object>?
Besides, when I'm trying to explicitly pass the type through a "Type" parameter, it just causes me compilation time error like this.
public void GetInstanceService(Type type){
List<type> type2 = new ArrayList<type>(); //Compilation time error
//Or
msgType oType = new msgType();
Class classType = oType.getClass();
List<classType> type3; //Compilation time error
}
So, if none of these attempts was effective, how could I set the type for deserialization?
Guava class TypeToken does not support that mode of usage. You are creating the type token with a type variable and there not enough information for it to reconstruct List<String> from List<T>. You should create an instance of TypeToken where you have all the required compile-time information.
The documentation says:
Note that it's critical that the actual type argument is carried by a
subclass. The following code is wrong because it only captures the <T>
type variable of the listType() method signature; while <String> is
lost in erasure:
class Util {
static <T> TypeToken<List<T>> listType() {
return new TypeToken<List<T>>() {};
}
}
TypeToken<List<String>> stringListType = Util.<String>listType();
But as said above, you can instantiate the TypeToken at call-site, where all type info are available, and then pass it as a parameter. Something like this:
public <thing> void GetInstanceService(String method, TypeToken<List<thing>> token){
List<thing> response = new ArrayList<thing>();
Type rType2 = token.getType();
type2 = uJSON.fromJson(new String(entity), rType2);
}
comm.GetInstanceService("listTestType", new TypeToken<List<msgType>>() {});
Update
Paul Bellora notes that you can also accept a parameter TypeToken<thing> token, and construct a TypeToken<List<thing>> inside the method from that token:
public <thing> void GetInstanceService(String method, TypeToken<thing> token) {
List<thing> response = new ArrayList<thing>();
Type rType2 = new TypeToken<List<thing>>() {}
.where(new TypeParameter<thing>() {}, token); // where() binds "thing" to token
.getType();
type2 = uJSON.fromJson(new String(entity), rType2);
}
comm.GetInstanceService("listTestType", new TypeToken<msgType>() {});
Due to something called type erasure, the class object you need is not available at runtime.
However, there is a standard work-around: pass a type token into your method, like this:
public <T> List<T> getService(String method, Class<T> c) {
// the caller has passed in the class object
List<T> list = new ArrayList<T>();
// fill list
return list;
}

Question about java generics

I am writing this query service that's suppose to work with couple predefined classes. However, it seems to me to be redundant that, when using this service class, I need to pass both the Type of the class and a class object it self. For example, to query "Contact" object, I'll need to provide both and Contact.class, like the following:
lookupService = new MyLookupServiceImpl<Contact>(Contact.class);
In this case, is there a way to initialize Class without passing in "Contact.class"?
The service class look like the following:
public class MyLookupServiceImpl<T> {
private Class<T> cls;
public MyLookupServiceImpl(Class<T> clz){
this.cls = clz;
}
public T queryObject(String sql) {
try {
QueryResult result = ConnectionFactory.getConnection().query(sql);
if(result.getSize()>0){
T obj=null;
MyObject sObj = result.getRecords()[0];
MyObjectBuilder<T> ob = new MyObjectBuilder<T>(cls);
obj = ob.buildORMObject(sObj);
return obj;
}
} catch (ConnectionException e) {
logger.warn(e.toString());
}
return null;
}
}
Any suggestion would be appreciated.
Thank you,
The answer is NO. In java, because of type erasure, there is no other way infer/get this info at runtime other that passing in the type info as a Class<?> instance.
This is most likely necessary because it is not possible to create a new instance of an arbitrary class indicated by a type parameter; in other words, you cannot do:
T obj = new T();
because of the way Java generics are implemented (with type erasure).
Note that in your code, cls is passed to MyObjectBuilder<T> which is most likely an object that creates new instances of T. MyObjectBuilder<T> uses reflection to create a new instance of T, with a statement like this:
T obj = cls.newInstance();
See also: Create new instance of T in Java
Bala is correct (+1) unfortunately. However, if you use a Dependency Injection framework like Guice you could ask Guice to pass you a TypeLiteral which is the type you're interested in. You could do something like this:
public class MyLookupServiceImpl<T> {
private Class<T> cls;
#Inject
public MyLookupServiceImpl(TypeLiteral<T> type){
this.cls = (Class<T>)type.getRawType();
}
}
Then use the Guice Injector to pass you instances of MyLookupServiceImpl. Just an idea! :)

Create Objects Dynamically in Java

Is there a (better) way to dynamically create Objects?
Right now I'm using a simple 'factory pattern' solution as following:
String classType = generalObject.getClass().toString();
if(classType.equals("class be.testApp.UserObject")) {
return UserObject.fromByteArray(data);
//return new UserObject();
}
else if(classType.equals("class.be.testApp.NewsObject")) {
return NewsObject.fromByteArray(data);
//return new NewsObject();
}
This code is not a factory pattern and no object is created. You evaluate the class name and call a static method on a class.
Now it looks like you have an object (generalObject) and want to create a new instance of the very same type. If all possible types have a public default constructor (convention!), then you can use this to create a new instance based on the given object:
Object newObject = generalObject.getClass().newInstance();
(but maybe I still didn't get your idea...)
You could use reflection here, something like
final Class<?> clazz = generalObject.getClass();
final Method method = clazz.getMethod("fromByteArray", data.getClass());
return method.invoke(null, data);
should do.
String classType = generalObject.getClass().toString();
if(classType.equals("class be.testApp.UserObject")) {
return UserObject.fromByteArray(data);
}else if(classType.equals("class.be.testApp.NewsObject")) {
return NewsObject.fromByteArray(data);
}
This is very complicated. Since apparently both classes are on your compile classpath, just use the class objects, not their string representations:
Class<?> classType = generalObject.getClass();
if(UserObject.class.equals(classType)) {
return UserObject.fromByteArray(data);
}else if(NewsObject.class.equals(classType)) {
return NewsObject.fromByteArray(data);
}

Categories

Resources