Ответ
Да, может. Один класс может реализовать как Runnable, так и Callable<V>. Это два отдельных интерфейса для представления задач в многопоточности.
| Различия интерфейсов: | Критерий | Runnable |
Callable<V> |
|---|---|---|---|
| Возвращаемое значение | void |
Тип V (обобщенный) |
|
| Исключения | Не может бросать проверяемые (checked) исключения | Может бросать Exception |
|
| Запуск | Передается в Thread или ExecutorService.execute() |
Передается в ExecutorService.submit() |
Пример класса, реализующего оба интерфейса:
import java.util.concurrent.Callable;
public class DualTask implements Runnable, Callable<String> {
private final String data;
public DualTask(String data) { this.data = data; }
@Override
public void run() {
// Логика для Runnable
System.out.println("Processing (Runnable): " + data);
}
@Override
public String call() throws Exception {
// Логика для Callable, может вернуть результат и бросить исключение
if (data == null) throw new IllegalArgumentException("Data is null");
return "Processed (Callable): " + data.toUpperCase();
}
}
Использование:
DualTask task = new DualTask("test");
// Как Runnable
new Thread(task).start();
// Или
executorService.execute(task);
// Как Callable
Future<String> future = executorService.submit(task); // Здесь task используется как Callable
String result = future.get(); // result = "Processed (Callable): TEST"
Важно: Класс FutureTask (часто используемая обертка) может принимать Callable в конструкторе и сам реализует Runnable, но это не стандартный класс, реализующий оба интерфейса "напрямую".