2

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);
1
  • did you have a warning when you compiled it? Commented Nov 30, 2013 at 8:50

4 Answers 4

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Then it should also work like I suggested to use an Parameter[], right?
@ManuelM. As far as I know. it will fail if the method call worker.doInBackground(...) expects a real Table[] instead of a type-erased Object[]. This often happens when you implement a generic interface, such as this ResponseWorker<...>
Yes, thats true. I think about moving from generics to Object in ResponseWorker.doInBackground(Object ... params) because I hate an extra parameter for the Class. On the other hand I hate to cast params to the correct object in doInBackground() :-)
This is wrong. The array is created by the code that calls the varargs method. In that scope, there is no ambiguity about what type of array to create.
1

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".

3 Comments

Actually he gives it as a param in the constructor (Table named t). But I agree very strongly with your "P" suggestion.
I have updated my question and have added the code Snipped for ResponseWorkerExecutor
"Unfortunately, it isn't smart enough to recognize the now erased type Parameter" That doesn't make sense. When it sees new ResponseWorkerRunnable<Table, SuperTable>(worker, t), the compiler knows exactly what Parameter is here, because it is explicitly given.
0

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.

Comments

0

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

1 Comment

I think this is pretty much "not solving the problem"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.