web-dev-qa-db-ja.com

引数付きの呼び出し可能なラムダ式

ジェネリックを使用して「TaskExecutor」を実装しようとしています(ジェネリックを使用する最初の試み)。ExecutorServiceを使用しています。

これが私の「TaskExecutor」クラスです。

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;
    }
}

これが私の実行方法です:

@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);
}

そしてここに私の質問:引数を受け入れるように私のCallableを書く方法i一行で:

Future<T> future = executor.submit(task);

例えば。望ましい結果は次のようになります:

Do something on 1
Do something on 3
Do something on 7
Do something on 2
<...etc...>

私のコードに何か他の問題がある場合は、私に知らせてください。

[〜#〜]編集[〜#〜]

削除された実装Callable

上記のコードは抽象化私が本当にやりたいことの:

  • IntRangeは、実際にはSQLからデータを取得するバッチのセットです。 Callableは、これらのSQLバッチを処理する方法のロジックを実際に実装します。

EDIT2

すべての提案の後、私は次の解決策を持っています:

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;
    }
}

そしてそれを実行する方法:

   @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;
        }
    }

EDIT

それを実行するさらに簡単な方法:

   @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);
    }

私は最終的な解決策に非常に満足していると思います。皆さんありがとう!

5
lapkritinis

これは一種の不可能です。ラムダの場合、変数を呼び出し可能オブジェクトに渡すことはできません。一方、Callableを実装し、変数のセッターを持つ独自の特定のオブジェクトを使用できます。

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;
    }

}

使用法:

@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);
}

または、セッターの代わりにコンストラクターでパラメーターを設定することもできます。

1
Tamas Rev

まず、call()はまったく必要ありません。また、Callable<T>をそのように使用することはないため、このクラスに実装する必要もありません。

呼び出し可能オブジェクトを希望どおりに作成するには、次のようにします。

Callable<String> task; // from the parameter
int i; // from the loop
Callable<String> wrapper = () -> { return task.call() + " on " + i; }
executor.submit(wrapper);

基本的に、ラムダに外部変数iをパラメーターとして指定します。

1
daniu