Callable lambda expression with argument
Solution 1
This is sort of impossible. You cannot pass a variable to a callable, if that's a lambda. On the other hand, you can use your own specific object that implements Callable
and has a setter for the variable:
public class CallableWithParam implements Callable<String> {
// protected for subclassing call()
// volatile for multi-threaded reasons
protected volatile int param = 0;
public void setParam(int param) {
this.param = param;
}
@Override
public String call() {
return "my param is: " + param;
}
}
Usage:
@Test
public void process() throws Exception {
CallableWithParam callable = new CallableWithParam() {
@Override
public String call() {
// an anonymous inner class is almost a lambda ;)
return "my param is: " + param + "in subclass";
}
};
callable.setParam(3);
ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>();
List<String> result = executeAlerterTask.process("TaskName", callable);
result.forEach(System.out::println);
}
Alternatively, you can set the param in the constructor instead of the setter.
Solution 2
Firstly, you don't need call()
at all, neither do you need to implement Callable<T>
in this class because you never use it as such.
To create a Callable
the way you want, you could do:
Callable<String> task; // from the parameter
int i; // from the loop
Callable<String> wrapper = () -> { return task.call() + " on " + i; }
executor.submit(wrapper);
You're essentially giving the lambda the outside variable i
as a parameter.
Comments
-
lapkritinis almost 2 years
I am trying to implement "TaskExecutor" with generics (my first attempt to use generics) and using ExecutorService.
Here is my "TaskExecutor" class:
public class ExecuteAlerterTask<T> { public List<T> process(String executorName, Callable<T> task) throws ExecutionException, InterruptedException { final ThreadFactory threadFactory = new ThreadFactoryBuilder() .setNameFormat(executorName + "-%d") .setDaemon(true) .build(); ExecutorService executor = Executors.newFixedThreadPool(10, threadFactory); Collection<Future<T>> futures = new ArrayList<>(); IntStream.range(1, 10).forEach(i -> { Future<T> future = executor.submit(task); futures.add(future); }); List<T> result = new ArrayList<>(); for (Future<T> f : futures) { result.add(f.get()); } executor.shutdown(); return result; } }
Here is my way to run it:
@Test public void process() throws Exception { Callable<String> callable = () -> "Do something on "; ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>(); List<String> result = executeAlerterTask.process("TaskName", callable); result.forEach(System.out::println); }
And here is my question: How to write my Callable that it would accept argument i at a line:
Future<T> future = executor.submit(task);
E.g. desired result would be:
Do something on 1 Do something on 3 Do something on 7 Do something on 2 <...etc...>
If something else is wrong with my code - please let me know.
EDIT
Removed implements Callable
Code above is abstraction of what I really want to do:
- IntRange really is set of batches where I get data from SQL. Callable really implements logic how to process those SQL batches.
EDIT2
After all suggestions I have following solution:
public class ExecuteAlerterTask<T> { public List<T> process(String executorName, Collection<Callable<T>> task) throws ExecutionException, InterruptedException { final ThreadFactory threadFactory = new ThreadFactoryBuilder() .setNameFormat(executorName + "-%d") .setDaemon(true) .build(); ExecutorService executor = Executors.newFixedThreadPool(10, threadFactory); Collection<Future<T>> futures = executor.invokeAll(task); List<T> result = new ArrayList<>(); for (Future<T> f : futures) { result.add(f.get()); } executor.shutdown(); return result; } }
And way to run it:
@Test public void process() throws Exception { Collection<Callable<String>> tasks = new ArrayList<>(); IntStream.range(1, 10).forEach(i -> { tasks.add(new Task(i).callable); }); ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>(); List<String> result = executeAlerterTask.process("TaskName", tasks); result.forEach(System.out::println); } private class Task { private int i; private Callable<String> callable = () -> "Doing something on i: " + i; private Task(int i) { this.i = i; } }
EDIT3
Even simpler way to run it:
@Test public void process() throws Exception { Collection<Callable<String>> tasks = new ArrayList<>(); IntStream.range(1, 10).forEach(i -> { tasks.add(() -> "Do something on i: " + i * 2); }); ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>(); List<String> result = executeAlerterTask.process("TaskName", tasks); result.forEach(System.out::println); }
I guess I quite happy with final solution. Thanks all!