LANG-1149

Ability to throw checked exceptions without declaring them
This commit is contained in:
Chas Honton 2015-07-11 15:41:56 -07:00
parent 1b066eb4f6
commit 59022fb870
3 changed files with 123 additions and 4 deletions

View File

@ -22,6 +22,7 @@
<body>
<release version="3.5" date="tba" description="tba">
<action issue="LANG-1149" type="add" dev="chas" due-to="Gregory Zak">Ability to throw checked exceptions without declaring them</action>
<action issue="LANG-1002" type="fix" dev="chas" due-to="Michael Osipov">Several predefined ISO FastDateFormats in DateFormatUtils are incorrect</action>
<action issue="LANG-1152" type="fix" dev="chas" due-to="Pas Filip">StringIndexOutOfBoundsException or field over-write for large year fields in FastDateParser</action>
<action issue="LANG-1153" type="add" dev="chas">Implement ParsePosition api for FastDateParser</action>

View File

@ -693,4 +693,70 @@ public class ExceptionUtils {
return getMessage(root);
}
/**
* Throw a checked exception without adding the exception to the throws
* clause of the calling method. This method prevents throws clause
* pollution and reduces the clutter of "Caused by" exceptions in the
* stacktrace.
* <p>
* The use of this technique may be controversial, but exceedingly useful to
* library developers.
* <code>
* public int propagateExample { // note that there is no throws clause
* try {
* return invocation(); // throws IOException
* } catch (Exception e) {
* return ExceptionUtils.rethrow(e); // propagates a checked exception
* }
* }
* </code>
* <p>
* This is an alternative to the more conservative approach of wrapping the
* checked exception in a RuntimeException:
* <code>
* public int wrapExample { // note that there is no throws clause
* try {
* return invocation(); // throws IOException
* } catch (Exception e) {
* throw new UndeclaredThrowableException(e); // wraps a checked exception
* }
* }
* </code>
* <p>
* One downside to using this approach is that the java compiler will not
* allow invoking code to specify a checked exception in a catch clause
* unless there is some code path within the try block that has invoked a
* method declared with that checked exception. If the invoking site wishes
* to catch the shaded checked exception, it must either invoke the shaded
* code through a method re-declaring the desired checked exception, or
* catch Exception and use the instanceof operator. Either of these
* techniques are required when interacting with non-java jvm code such as
* Jyton, Scala, or Groovy, since these languages do not consider any
* exceptions as checked.
*
* @since 3.5
*
* @param throwable
* The throwable to rethrow.
* @return R Never actually returns, this generic type matches any type
* which the calling site requires. "Returning" the results of this
* method, as done in the propagateExample above, will satisfy the
* java compiler that all code paths return a value.
* @throws throwable
*/
public static <R> R rethrow(Throwable throwable) {
// claim that the typeErasure invocation throws a RuntimeException
return ExceptionUtils.<R, RuntimeException> typeErasure(throwable);
}
/**
* Claim a Throwable is another Exception type using type erasure. This
* hides a checked exception from the java compiler, allowing a checked
* exception to be thrown without having the exception in the method's throw
* clause.
*/
@SuppressWarnings("unchecked")
private static <R, T extends Throwable> R typeErasure(Throwable throwable) throws T {
throw (T) throwable;
}
}

View File

@ -16,11 +16,15 @@
*/
package org.apache.commons.lang3.exception;
import org.junit.After;
import org.junit.Test;
import org.junit.Before;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
@ -28,6 +32,11 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.List;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Tests {@link org.apache.commons.lang3.exception.ExceptionUtils}.
*
@ -535,4 +544,47 @@ public class ExceptionUtilsTest {
public NestableException(final Throwable t) { super(t); }
}
@Test
public void testThrow() {
Exception expected = new InterruptedException();
try {
ExceptionUtils.rethrow(expected);
Assert.fail("Exception not thrown");
}
catch(Exception actual) {
Assert.assertSame(expected, actual);
}
}
@Test
public void testCatchTechniques() {
try {
throwsCheckedException();
Assert.fail("Exception not thrown");
}
catch(Exception ioe) {
assertTrue(ioe instanceof IOException);
assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
}
try {
redeclareCheckedException();
Assert.fail("Exception not thrown");
}
catch(IOException ioe) {
assertEquals(1, ExceptionUtils.getThrowableCount(ioe));
}
}
private static int redeclareCheckedException() throws IOException {
return throwsCheckedException();
}
private static int throwsCheckedException() {
try {
throw new IOException();
} catch (Exception e) {
return ExceptionUtils.rethrow(e);
}
}
}