2

I try to implement an inner method in order to execute the following code in a new thread

MyPojo result = null;
final MyPojo result2 = result;

FutureTask<MyPojo> runnableTask = new FutureTask<MyPojo>(
    new Runnable() {  

        BindJSON<MyPojo> binding;

        // Make the URL at which the product list is found
        String sourceURLString = 
            "http://www.....ca/files/{CAT_ID}.json";                

        @Override
        public void run() {  
            sourceURLString = sourceURLString.replace("{CAT_ID}", catId);
            binding = new BindJSON<MyPojo>();  
            result2 = binding.downloadData(sourceURLString, MyPojo.class);  
        }  
    }, 
    result2);

runnableTask.run();

So, now I take an error which says: The final local variable result2 cannot be assigned, since it is defined in an enclosing type. I take a look at this answer: Cannot refer to a non-final variable i inside an inner class defined in a different method but it didn't work for me. What should I do to make this work?

Community
  • 1
  • 1
Nick Robertson
  • 1,047
  • 4
  • 18
  • 41

2 Answers2

4

You might want to use a Callable, not a Runnable:

// the variable holding the result of a computation
String result = null;

FutureTask<String> runnableTask = new FutureTask<String>(
        new Callable<String>() {
            public String call() throws Exception {
                // (asynchronous) computation ...
                return "42";
            }
        });

System.out.println("result=" + result); // result=null

// this will invoke call, but it will all happen in the *same thread*
runnableTask.run();

// to have a parallel thread execute in the 'background'
// you can use java.util.concurrent.Executors
// Note: an ExecutorService should be .shutdown() properly
// Executors.newSingleThreadExecutor().submit(runnableTask);

// waits for the result to be available
result = runnableTask.get();

// you can also add timeouts:
// result = runnableTask.get(100, TimeUnit.MILLISECONDS);

System.out.println("result=" + result); // result=42
miku
  • 181,842
  • 47
  • 306
  • 310
3

Your use of the FutureTask is not very useful because you execute it in the same thread. You could use an executor and submit a callable to achieve what you want:

    ExecutorService executor = Executors.newFixedThreadPool(1);

    Callable<MyPojo> task = new Callable<MyPojo> () {
        BindJSON<MyPojo> binding;
        // Make the URL at which the product list is found
        String sourceURLString = "http://www.....ca/files/{CAT_ID}.json";

        @Override
        public MyPojo call() {
            sourceURLString = sourceURLString.replace("{CAT_ID}", catId);
            binding = new BindJSON<MyPojo>();
            return binding.downloadData(sourceURLString, MyPojo.class);
        }
    };

    Future<MyPojo> future = executor.submit(task);
    MyPojo result = future.get();

Note: the call to future.get(); will block until the task completes.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • I have a similar use case, trying to convert AsyncTasks to ExecutorService to return a List from a Room database. Another answer suggests using submit() on ExecutorService and get() on the Dao method (see stackoverflow.com/questions/52242933/room-database-query). Would appreciate your thoughts on that approach versus the approach using a Callable and a Future. – AJW Apr 13 '21 at 15:57
  • 1
    @AJE I suggest you ask a separate question with the relevant details. – assylias Apr 14 '21 at 08:43