I've got several thread classes that use different comparison methods. I've implemented them as extending an abstract class. For example,
public abstract class MatcherThread implements Runnable{
List<String> clusters;
int output;
public MatcherThread(List<String> clusters){
this.clusters = clusters;
}
public void run()
{
for(List<String> c: clusters) {
compare(c);
}
}
public int getOutput(){
return output;
}
protected abstract void compare(String c);
}
public class MaxLength extends MatcherThread{
public MaxLength(List<String> clusters){
super(clusters);
this.output = Integer.MAX_VALUE;
}
protected void compare(String c){
if(c.length() > output) output = c.length();
}
}
public class MinLength extends MatcherThread{
public MinLength(List<String> clusters){
super(clusters);
this.output = 0;
}
protected void compare(String c){
if(c.length() < output) output = c.length();
}
}
Now, I want to have a class which can run either thread. My first thought was to make this class generic, but distributing work to the threads requires instantiating them.
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Matcher<T extends MatcherThread>{
protected List<Integer> runAll(List<String> clusters, int nthreads) {
int n = clusters.size();
int poolSize = nthreads;
int step = n/poolSize;
ExecutorService es = Executors.newFixedThreadPool(poolSize);
List<T> tasks = new ArrayList<T>();
for (int i = 0; i < poolSize; i++) {
int start = i*step;
int end = i == poolSize -1 ? n: (i+1)*step;
List<List<String>> subcluster = new ArrayList<List<String>>(){{
for (int ind=start; ind < end; ind++) add(clusters(ind));
}};
T task = new T(subcluster); //This is not allowed.
tasks.add(task);
}
CompletableFuture<?>[] futures = tasks.stream().map(task -> CompletableFuture.runAsync(task, es)).toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();
es.shutdown();
List<Integer> output = new List<Integer>();
for(T t : tasks) {
output.add(t.getOutput());
}
return output;
}
}
How can I redesign my classes, so that instantiating a generic type is not necessary, but I can still switch easily between comparison functions?