Скажем, мне нужен класс, ограниченный общим типом Comparable
:
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) {
return someExecutorService.submit(task);
}
Теперь, есть ли возможность ограничить ввод метода submit
только тем, который принимает Callables
, который также реализует T
? Я сначала попробовал это:
<V, W extends T & Callable<V>> Future<V> submit(W task) {
if(compareSomething(task) != 0)
throw new RuntimeException("");
return someExecutorService.submit(task);
}
но выяснил, что это невозможно (по причинам, описанным здесь). Есть ли какая-то элегантная возможность обойти его?
РЕДАКТИРОВАТЬ: Одна уродливая возможность, о которой я могу думать, состоит в том, чтобы разбить инкапсуляцию на два разных типа и передать пару объектов в 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);
}
}
Основной недостаток заключается в том, что сигнатура метода становится более сложной, также мне потребуется некоторое взаимно однозначное отображение T
to Callable
для некоторого последнего кода. Может быть, можно предложить образец, который решает его соответствующим образом?..
ИЗМЕНИТЬ, возьмите два: позвольте мне вкратце объяснить, чего я пытаюсь достичь. Я работаю над пользовательской реализацией пула потоков, которая может выполнять какое-то специальное планирование задач. Для этого служба принимает только один специальный вид задач Callable
. Те Callable
должны реализовать пользовательский интерфейс, похожий на Comparable
. Сравнивая пары задач с использованием методов в этом интерфейсе, служба будет:
- Заблокировать входящую задачу, если она заблокирована любой выполняющейся задачей.
- Вызов ожидающих задач при завершении выполненной задачи
Future
. - Определите порядок выполнения ожидающих задач, сравнив их.
Логика блокировки/сравнения должна предоставляться самими задачами. Таким образом, класс пула потоков должен определять только тот тип Comparable
, который принимает объект пула, и его вообще не волнует, что это за тип Callable
и каков его тип возврата.
EDIT, возьмите три: на основе Эрик Робертсон ответ, теперь можно предотвратить подачу вонючего задачи:
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;
}
}
Приятно и легко! Надеюсь, когда-нибудь это поможет кому-то в той же ситуации, что и моя.: -)