How to pass a callable object as runnable object? Can we type cast Callable to Runnable?

62 views Asked by At

How is the below structure possibly working? Please explain.

Thanks in advance.

public abstract class AbstractCallableClass {
    protected abstract V getCallable();

    public void passRunnable() {
        runnableParamterised(this.getCallable());
    }
    
    public void runnableParamterised(Runnable runnable) {
        System.out.println(runnable.toString());
    }
}

class OneCallableClass implements Callable {
    @Override
    public Object call() throws Exception {
        return "Any dummy response";
    }
}

class TestClass extends AbstractCallableClass {
    @Override
    protected V getCallable() {
        return new OneCallableClass();
    }
}

It's not working as it is mentioned in the above sample. But apparently, the Callable is getting typecast-ed as Runnable when passed as an argument and is working in production.

2

There are 2 answers

0
Chaosfire On

You can't cast Callable to a Runnable - it will lead to ClassCastException, unless the callable is also a runnable (imagine a class implementing both interfaces).

You can implement a simple adapter to wrap a callable and pass it as a runnable, where one is required:

public class RunnableAdapter implements Runnable {

  private final Callable<?> callable;

  public RunnableAdapter(Callable<?> callable) {
    this.callable = callable;
  }

  @Override
  public void run() {
    try {
      callable.call();
    } catch (Exception exc) {
      throw new RuntimeException(exc);
    }
  }
}

Usage:

runnableParamterised(new RunnableAdapter(callable));
0
jaco0646 On

You can't cast Callable to a Runnable - it will lead to ClassCastException, unless the callable is also a runnable (imagine a class implementing both interfaces).

+1

Implementing both interfaces (a two-way Adapter) is also pretty straightforward. The obvious consequences of invoking a Callable as a Runnable are that you lose its return value and wrap its Exceptions.

@FunctionalInterface
public interface CallableRunnable<R> extends Callable<R>, Runnable {
    static <R> CallableRunnable<R> fromCallable(Callable<R> callable) {
        return callable::call;
    }

    static CallableRunnable<Void> fromRunnable(Runnable runnable) {
        return () -> { runnable.run(); return null; };
    }

    @Override
    default void run() {
        try {
            call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

// Tests ----------------------------------------------------------------------------------
    static void main(String... args) throws Exception {
        Callable<String> callable = () -> "Hello Callable!";
        Runnable runnable = () -> System.out.println("Hello Runnable!");

        Callable<Void> adaptedRunnable = CallableRunnable.fromRunnable(runnable);
        Runnable adaptedCallable = CallableRunnable.fromCallable(callable);

        System.out.println(adaptedRunnable.call());  // null
        adaptedCallable.run();

        Callable<String> thrower = () -> { throw new Exception("Goodbye Callable."); };
        Runnable adaptedThrower = CallableRunnable.fromCallable(thrower);
        try {
            adaptedThrower.run();
        } catch (RuntimeException e) {
            System.out.println(e.getCause().getMessage());
        }

        String msg = "Hello CallableRunnable!";
        CallableRunnable<String> callableRunnable = () -> { System.out.println(msg); return msg; };
        System.out.println(testMethodRequiresCallable(callableRunnable));
        testMethodRequiresRunnable(callableRunnable);
    }

    static <R> R testMethodRequiresCallable(Callable<R> callable) throws Exception {
        return callable.call();
    }

    static void testMethodRequiresRunnable(Runnable runnable) {
        runnable.run();
    }
}