简体   繁体   中英

Propagating an exception from a generic method without throws declaration

I have a simple interface

public interface Func<I, O> {
    public O apply(I i);
}

I have a class with a bunch of private (static) classes that implement that interface like so

import org.json.*;
public static class Baz {
    public static B Qux(String jsonSource) throws JSONException {
        JSONObject m = new JSONObject(jsonSource);
        return new Baz.Foo().apply(m);
    }

    private static class Foo implements Func<JSONObject, B> {
        public B apply(JSONObject f) {
            JSONObject x = f.getJSONObject("bar");
            /* Whatever other code in here */
            return new B(); /* Place-holder */
        }
    }
}

As pretty much any operation on JSONObjects requires that JSONException is caught, the compiler complains about an uncaught and undeclared exception at JSONObject x = f.getJSONObject("bar"); . Thinking that I could simply wrap that particular apply in try-catch and rethrow the exception to the caller, I tried it, as follows:

private static class Foo implements Func<JSONObject, B> {
    public B apply(JSONObject f) {
        try {
            JSONObject x = f.getJSONObject("bar");
            /* Whatever other code in here */
            return new B(); /* Place-holder */
        catch (JSONException e) {
            throw e;
        }
    }
}

With this however, I now get error: unreported exception JSONException; must be caught or declared to be thrown error: unreported exception JSONException; must be caught or declared to be thrown pointing at throw e; .

I don't understand why this doesn't work. Further wrapping the call to apply did nothing new, as expected.

My questions are:

  1. Why doesn't this work?
  2. How can I fix it?

I believe a workaround would be to have my interface accept an exception type. I'm not sure whether this syntax will work but I imagine that I can do something along the lines of

public interface FuncEx<I, O, E> {
    public O apply(I i) throws E;
}

Is there A Better Way™? I don't imagine that I'd have to create a whole new interface just to tack on this small bit.

Checked exceptions (ie Exceptions that are NOT a sublcass of RuntimeException - like JSONException) form part of a method's signature. If you call a method that throws a checked exception you must handle it, or declare that you yourself throw it. If you throw a checked exception yourself (like you did with your catch/rethrow code) you must still declare it.

If you don't want to declare that you throw it then you can wrap it in a RuntimeException. eg:

private static class Foo implements Func<JSONObject, B> {
  public B apply(JSONObject f) {
    try {
        JSONObject x = f.getJSONObject("bar");
        /* Whatever other code in here */
        return new B(); /* Place-holder */
    catch (JSONException e) {
        throw new RuntimeException("Could not get bar", e);
    }
  }
}

The thoughts on when to throw checked Exceptions as opposed to RuntimeExceptions are many and varied. For me, my first guiding principle is if you can reasonably expect the caller to respond to the exception in a meaningful way (not just rethrow it) then make it a checked exception. Otherwise make it a RuntimeException.

This is an excellent article on the topic: http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM