Let's say I need a class that is bounded to a generic Comparable
type:
class A<T extends Comparable<T>> {
// this is just an example of usage of T type
List<T> comparables;
int compareSomething(T smth) {
return comparables.get(0).compareTo(smth);
}
}
The class has a method with own generics in signature:
<V> Future<V> submit(Callable<V> task) {
return someExecutorService.submit(task);
}
Now, is there a possibility to restrict the input of the submit
method to accept only Callables
that also implement T
? I first tried this:
<V, W extends T & Callable<V>> Future<V> submit(W task) {
if(compareSomething(task) != 0)
throw new RuntimeException("");
return someExecutorService.submit(task);
}
but found out it's not possible (for reasons described here). Is there any elegant possibility to bypass it?
EDIT: The one ugly possibility I can think about is to split the encapsulation in two different types and pass an object pair in submit
:
class A<T extends Comparable<T>> {
// this is just an example of usage of T type
List<T> comparables;
int compareSomething(T smth) {
return comparables.get(0).compareTo(smth);
}
<V> Future<V> submit(Callable<V> task, T comparable) {
if(compareSomething(comparable) != 0)
throw new RuntimeException("");
return someExecutorService.submit(task);
}
}
The main disadvantage is that the method signature becomes more complicated, also I will need some one-to-one mapping of T
s to Callable
s for some latter code. Maybe one can suggest a pattern that solves it in an appropriate way?..
EDIT, take two: Let me explain briefly what I'm trying to achieve. I'm working on a custom thread pool implementation that is able to perform some kind of special task scheduling. To do so, this service accepts only one special sort of Callable
tasks. Those Callable
s have to implement a custom interface that is similar to the Comparable
one. By comparing pairs of tasks using methods in this interface, the service will:
- Block the incoming task if it is blocked by any running task.
- Invoke pending tasks on completion of a done task's
Future
. - Determine the execution order of pending tasks by comparing them.
The blocking/comparison logic should be provided by the tasks themselves. This way the thread pool class should only define what special kind of Comparable
s the pool object is accepting, and it doesn't care at all what kind of Callable
s they really are and what is their return type.
EDIT, take three: Based on Erick Robertson's answer, it's now possible to prevent submitting of smelly tasks:
public static void test(String[] args) {
A<Valid> scheduler = new A<>();
scheduler.betterSubmit(new Valid()); // applies to method signature
scheduler.betterSubmit(new Forbidden()); // rejected on compile time
scheduler.betterSubmit(new ConformWithValid()); // still appliable because all required interfaces implementations recognised
}
// just a bunch of test classes
private static class Valid implements Comparable<Valid>, Callable<Void> {
@Override
public int compareTo(Valid o) {
return 0;
}
@Override
public Void call() throws Exception {
return null;
}
}
private static class Forbidden implements Comparable<Forbidden>, Callable<Void> {
@Override
public int compareTo(Forbidden o) {
return -1;
}
@Override
public Void call() throws Exception {
return null;
}
}
private static class ConformWithValid implements Comparable<Valid>, Callable<Boolean> {
@Override
public int compareTo(Valid o) {
return 1;
}
@Override
public Boolean call() throws Exception {
return Boolean.FALSE;
}
}
Nice and easy! Hope some day this will help someone in the same situation as mine. :-)