Search code examples
javasyntaxstring-conversion

"Unhandled exception type" when Throwing exception inside a compact structure (StringConverter case)


I believe this must be a syntax ignorance on my part.

I want to make a method that creates and return a StringConverter object. The problem is that this object must throw an exception in one of its methods. And I believe I have been failing because syntax.

I've two exemples, the last one compiles just fine.

Taking a look in the first method, Eclipse says "Unhandled exception type CharConversionException":

public static StringConverter<String> creditoDebito() {
        return new StringConverter<String>() {
            @Override
            public String fromString(String arg0) {
                switch(arg0.charAt(0)) {
                case 'D':
                case 'd':
                    return "Débito";
                default:
                    throw new CharConversionException();
                }
            }
        }; 
    }

Taking a look in one the successful exemples, it compiles just fine:

public static StringConverter<BigDecimal> numberCellConverter(){
    return new StringConverter<BigDecimal>() {
        @Override
        public BigDecimal fromString(String arg0) {
            Pattern patternDefault = Pattern.compile("\\d*");
            Matcher matcher = patternDefault.matcher(arg0);
            if(matcher.find()) {
                return new BigDecimal(arg0);
            }else {
                throw new NumberFormatException();
            }
        }
    };
}

What the difference between those two cases? Why NumberFormatException compiles while the other doesn't?


Solution

  • Exceptions aren't just a funny name. You shouldn't use an exception just because the name sounds vaguely relevant. CharConversionException is about issues with charset encoding and e.g. XML reading; the point is, it extends IOException, so it is a kind of 'problem with an Input/Output channel', and it just doesn't apply here. Make your own exception types, or use one of the more general exception types that are intentionally designed (by reading the javadoc and checking the inheritance) to be a bit more nebulous. For example, IllegalArgumentException could be used here (if the argument doesn't start with a D or d, it is, apparently, illegal? Bit of a weird method).

    The difference is in the kind of exception it is.

    In java, throwables are checked, meaning, a method needs to catch all exceptions it throws, unless it explicitly decrees that it, itself, throws this. Thus, this does not compile:

    public void test() {
        throw new IOException();
    }
    

    whereas this does:

    public void test() throws IOException {
       throw new IOException();
    }
    

    And note that by invoking a method that declares that it throws something, you inherit this 'catch it or declare that you rethrow it'. Thus, this does not compile:

    public void test2() {
       test(); // declared to `throws IOException`
    }
    

    whereas this does:

    public void test2() throws IOException {
       test();
    }
    

    And note that public static void main(String[] args) is allowed to (and usually should!) be declared to just throws Exception.

    Java has another rule baked in that explains why NumberFormatException gets a pass: All methods are assumed to throws RuntimeException, Error (as in, can throw both RuntimeException and Error instances), whether you write this or not.

    Check the hierarchy of NumberFormatException: It is a specialization of RuntimeException, and thus, inherently all methods are declared to throws is. CharConversionException extends IOException extends Exception extends Throwable - nothing in that list is either RuntimeException or Error and therefore no pass is allowed.

    To make matters worse, if you are implementing a method from a supertype (such as here; you are overriding the public String fromString(String arg) method), you can't just add new methods in your declaration. After all, you are writing a kind of StringConverter, which means anybody can treat an instance of your newly defined class as a StringConverter, and StringConverter's fromString method doesn't declare it, thus, you're stuck.

    Fortunately, as I said, CharConversionException is not appropriate, but IllegalArgumentException might be, and IllegalArgumentException is unchecked (it extends RuntimeException, thus, you can freely throw it as all methods are silently assumed to do that).