Why cannot run() of Runnable throw checked Exceptions?

心已入冬 提交于 2019-12-03 10:07:32
Peter Lawrey

It cannot throw a checked exception because it wasn't declared as throwing a checked exception from the first version and it is too dangerous to change it.

Originally Runnable was only used in a wrapped Thread, and it was assumed the developer would want to catch all checked exceptions and handle them rather than logging them to System.err.

Callable was added when you can add individual tasks to an Executor where you can capture the result in a Future and any exception thrown.

Callable now allows you to return a value and optional declare a checked exception.

BTW: One way you can say you don't want a return or throw a checked exception from a callable is to use something like

Callable<Void> callable = new Callable<Void>() {
    public Void call() {
        // do something
        return null;
    }
};
Tom Anderson

This is not an answer to the question. Rather, it is a followup to Lukas Eder's answer, showing another way to smuggle a checked exception into a place where it is not statically allowed. This relies on the fact that if a no-argument constructor is invoked with newInstance, any checked exception it throws escapes upward.

public class Thrower {

    private static final ThreadLocal<Exception> toThrow = new ThreadLocal<Exception>();

    public static void throwUnsafely(Exception e) {
        try {
            toThrow.set(e);
            Thrower.class.newInstance();
        } catch (InstantiationException f) {
            throw new RuntimeException("unexpected exception while throwing expected exception", f);
        } catch (IllegalAccessException f) {
            throw new RuntimeException("unexpected exception while throwing expected exception", f);
        } finally {
            toThrow.remove();
        }
    }

    private Thrower() throws Exception {
        throw toThrow.get();
    }

}

This is class-A truly ancient black hat Java voodoo. Do not ever do this. Except at parties to impress people.

run() can't throw a checked exception because it is not declared to do so. You can't throw checked exceptions without declaring them.

You also can't declare checked exceptions on a method which overrides or implements another method which doesn't throw that exception. So, implementations of Runnable can't simply add throws clauses to their implementations of run().

If you look at the Runnable Interface you find that void run() method is not declared as throwing any checked exception and your Thread class implements Runnable Interface .

JLS says that method m1 cannot throw exception if in Interface/Superclass it is not declared.

I think the motivation behind keeping the signature void run() in Runnable is that it is not meant to be invoked like other methods, instead it is designed to be invoked by the CPU thread scheduler. If so, who is going to receive its return value and who is going to handle thrown checked exception by this. UncaughtExceptionHandler came in Java 5.0 to handle uncaught exceptions thrown by a Thread. The Executor Framework saves the returned value or thrown exception( wrapper in ExecutionException) as the states of some Object shared across threads(like Outer class instance) and purveys those to the invoker(who is running in some other thread) of Future.get().

You can always unsafely throw checked exceptions:

import java.lang.reflect.Field;
import sun.misc.Unsafe;

public class UnsafeSample {
    public void methodWithNoDeclaredExceptions( ) {
        Unsafe unsafe = getUnsafe();
        unsafe.throwException( new Exception( "this should be checked" ) );
    }

    private Unsafe getUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main( String[] args ) {
        new UnsafeSample().methodWithNoDeclaredExceptions();
    }
}

See the full article here:

http://java.dzone.com/articles/throwing-undeclared-checked.

Another alternative:

public class Test {
    public static void main(String[] args) {
        doThrow(new SQLException());
    }

    public static void doThrow(Exception e) {
        Test.<RuntimeException> doThrow0(e);
    }

    @SuppressWarnings("unchecked")
    public static <E extends Exception> void doThrow0(Exception e) throws E {
        throw (E) e;
    }
}

This was shown here:

http://java.dzone.com/articles/throw-checked-exceptions

Having said so, don't do it! ;-)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!