LANG-1149
Ability to throw checked exceptions without declaring them
This commit is contained in:
parent
1b066eb4f6
commit
59022fb870
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue