In a project I am working at, I have found a class which wraps all methods of its super-class in some elaborate exception handling. It looks similar to that:
public void method1() throws ExceptionA {
String exceptionString = "";
try {
super.method1();
} catch (ExceptionA e) {
exceptionString = // <convert the exception to string in an elaborate way>
throw e;
} finally {
// <an elaborate logger call which uses value of exceptionString>
}
}
public void method2() throws ExceptionB, ExceptionC {
String exceptionString = "";
try {
super.method2();
} catch (ExceptionB | ExceptionC e) {
exceptionString = // <convert the exception to string in elaborate way>
throw e;
} finally {
// <an elaborate logger call which uses value of exceptionString>
}
}
// ... <a bunch of other methods like this>
I immediately though "Wow, how could would it be to have one generic wrapper and just call it in every of these methods. The class would be like 10x shorter!".
So I got to work.
This is where I got stuck:
private interface ThrowingMethod<E extends Exception> {
void run() throws E;
}
public <E extends Exception> void wrapMethod(ThrowingMethod<E> method) throws E {
String exceptionString = "";
try {
method.run();
} catch (Exception e) {
exceptionString = // <convert the exception to string in an elaborate way>
throw e;
} finally {
// <an elaborate logger call which uses value of exceptionString>
}
}
public void method1() throws ExceptionA {
wrapMethod(super::method1); // works
}
public void method2() throws ExceptionB, ExceptionC {
wrapMethod(super::method2); // Error in Eclipse: "Unhandled exception type Exception"
}
// ... <a bunch of other methods like this>
In conclusion, this approach works for methods that throws only one type of checked exception. When method throws multiple checked exceptions, Java assumes that the exception type is Exception.
I tried to add more generic parameters to ThrowingMethod and wrapMethod but it doesn't change anything.
How can I get a functional interface to work with multiple generic exceptions?
When you expand your interface to use two type variables, i.e.
private static interface ThrowingMethod<E1 extends Exception,E2 extends Exception> {
void run() throws E1, E2;
}
public <E1 extends Exception,E2 extends Exception>
void wrapMethod(ThrowingMethod<E1,E2> method) throws E1,E2 {
// same as before
}
the rules regarding the type inference do not change and they are the same for both type variables. E.g. you can still use
public void method1() throws ExceptionA {
wrapMethod(super::method1);
}
as before, as the compiler simply infers the same single exception type for both type variables.
For the method declaring two exceptions, it won’t pick up one for the first type variable and the other for the second; there is no rule which could tell the compiler which exception to use for which type variable.
But you can help the compiler out in this case, e.g.
public void method2() throws ExceptionB, ExceptionC {
wrapMethod((ThrowingMethod<ExceptionB, ExceptionC>)super::method2);
}
which is the best you can get with this approach.
So your goal is just to wrap a bunch of methods with logging? A typical way to handle this is with AOP. You'd just create a single pointcut that matches all those methods, and you wouldn't have a bunch of repeated boilerplate. No need for those interfaces or wrapping methods.
Related
I have this code which it needs to throw and exception inside a Lambda:
public static <T, E extends Exception> Consumer<T> consumerWrapper(
Consumer<T> consumer,
Class<E> clazz) {
return i -> {
try {
consumer.accept(i);
} catch (Exception ex) {
try {
E exCast = clazz.cast(ex);
System.err.println(
"Exception occured : " + exCast.getMessage());
} catch (ClassCastException ccEx) {
throw ex;
}
}
};
}
public static void processConditions(#NotNull List<EntityCondition> conditions)
throws UnsatisfiedConditionException {
conditions.forEach(
consumerWrapper(
entityCondition -> {
throw new UnsatisfiedConditionException(entityCondition);
}, UnsatisfiedConditionException.class));
}
Even with this approach, compiling this would throw an error:
UnsatisfiedConditionException; must be caught or declared to be thrown
What could be wrong or missing here?
If you wrote out the lambda as an anonymous class instead
new Consumer<EntityCondition>() {
#Override public void accept(EntityCondition entityCondition) {
throw new UnsatisfiedConditionException(entityCondition);
}
}
...then it would make complete sense that it doesn't compile: the method accept in Consumer is not defined to throw an exception. It would have to be public void accept(..) throws UnsatisfiedConditionException { ... } or the like.
So what you need can't be Consumer. You have to make your own functional interface.
interface ThrowingConsumer<T> {
void accept(T t) throws Exception;
}
... and then convert it to a Consumer:
public static <T, E extends Exception> Consumer<T> consumerWrapper(
ThrowingConsumer<T> consumer,
Class<E> clazz) { ... }
...except that you can't throw ex, because that would break it again. You will have to decide what the appropriate behavior when you can't throw that exception is.
Lambdas have to be treated like anonymous classes.
Let's say that there is an interface called AbstractClass.
public interface AbstractClass {
void requestWork();
}
Creating an implementation of it would look like this:
public static AbstractClass getImplementation() {
return new AbstractClass() {
#Override
public void requestWork() {
throw new Exception(); // <- javac: "Unhandled exception: java.lang.Exception"
}
};
}
The lambda-eqivalent would look like this:
public static AbstractClass getImplementation() {
return () -> {
throw new Exception();
};
}
The compiler can't add a throws-declaration to the overriden method since the method is declared in the abstract class.
How to fix this? You have three options:
Add a throws declaration in the class that is implemented by the lambda
Use try-catch statements to handle the problem in the lambda
Use a subclass of RuntimeException since the Java compiler won't force you to handle those.
You can also catch the exception and rethrow it as a RuntimeException like this:
try {
throw new Exception();
} catch (Exception e) {
throw new RuntimeException(e);
}
Stop using your shiny fancy new hammer to smear butter on your toast. It works, sure, but not very well. Calling forEach on a Collection instance is not a good idea unless you already have the lambda from someplace else. list.forEach(x -> { .. }) is bad code style. Jst use for( var x: list) {} instead. These forms are just as short, there is no performance benefit whatsoever to a blind forEach, and the forEach has a ton of downsides that the for does not: The lambda is not (mutable) local variable transparent, not control flow transparent, and not checked exception transparent. All serious issues, as you're noticing.
forEach on streams is fine as a final terminal operation after a bunch of map, filter, flatMap, etc operations. It's not a global 'stop using foreach forever! kind of thing'.
Yeah yeah. Answer my question
Well, hey, your funeral.
Lambdas are not valid in java unless in a context that makes clear which FunctionalInterface they are filling in for. In this case, it's the first arg to consumerWrapper (which does nothing useful, I'll get to this in a second), and that takes a Consumer<T>. Let's look at the definition of Consumer:
package java.util.function;
#FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Therefore, your lambda is interpreted as an impl of that accept method. Notably, that means it can't throw anything (except RuntimeExceptions and Errors, as everything is allowed to throw these).
Hence, this is just not going to work. As long as you insist on using Consumer<T> as an input you're dead in the water.
Try this:
#FunctionalInterface
public interface ThrowingConsumer<T, E extends Throwable> {
void accept(T t) throws E;
public static Consumer<T> withRethrownExceptions(ThrowingConsumer<T> in) {
return t -> {
try {
in.accept(t);
} catch (RuntimeException e) { throw e; }
} catch (Error e) { throw e; }
} catch (Throwable t) throw new RuntimeException("Uncaught", t); }
};
}
The code below gives a checked error to throws Exception:
import java.io.IOException;
interface some {
void ss99() throws IOException;
}
public class SQL2 implements some {
#Override
public void ss99 () throws Exception {}
// ...
}
while the one below compiles fine:
import java.io.IOException;
interface some {
void ss99() throws IOException;
}
public class SQL2 implements some {
#Override
public void ss99 () throws NullPointerException {}
// ...
}
On what logic is Java doing this-- any ideas?
TIA.
The throws keyword indicates that a method or constructor can throw an exception, although it doesn't have to.
Let's start with your second snippet
interface some {
void ss99() throws IOException;
}
public class SQL2 implements some {
#Override
public void ss99 () throws NullPointerException {}
}
Consider
some ref = getSome();
try {
ref.ss99();
} catch (IOException e) {
// handle
}
All you have to work with is with your interface some. We (the compiler) don't know the actual implementation of the object it is referencing. As such, we have to make sure to handle any IOException that may be thrown.
In the case of
SQL2 ref = new SQL2();
ref.ss99();
you're working with the actual implementation. This implementation guarantees that it will never throw an IOException (by not declaring it). You therefore don't have to deal with it. You also don't have to deal with NullPointerException because it is an unchecked exception.
Regarding your first snippet, slightly changed
interface some {
void ss99() throws IOException;
}
public class SQL2 implements some {
#Override
public void ss99 () throws Exception { throw new SQLException(); }
}
Consider
some ref = new SQL2();
try {
ref.ss99();
} catch (IOException e) {
// handle
}
So although you are handling the exception declared in the interface, you would be letting a checked exception, SQLException, escape unhandled. The compiler cannot allow this.
An overriden method must be declared to throw the same exception (as the parent) or one of its subclasses.
According to
Cannot Create, Catch, or Throw Objects of Parameterized Types (Java Tutorials):
You can, however, use a type parameter in a throws clause:
class Parser<T extends Exception> {
public void parse(File file) throws T { // OK
// ...
}
}
But why would you want to? You can't construct T here. If you inject T after building it outside, isn't its stack trace going to be all wrong? Were they simply documenting a feature that happened to work regardless of it's usefulness?
Why not
class FooParser extends Parser<FooException> {
public void parse(File file) throws FooException {
throw new FooException("Not Supported");
}
}
You could have
public class ExceptionWithPayload<T> extends Exception {
private final T payload;
public ExceptionWithPayload(T payload) {
this.payload = payload;
}
public T getPayload(){
return payload;
}
}
and then in some other class, you could write
throw new ExceptionWithPayload<MyClass>(myObject);
so as to be able to pass any object you like back to the thing that catches the exception, but with type checking in the catch clause.
You can throw checked exceptions where it was not expected with the sneaky throw trick: http://rdafbn.blogspot.hu/2013/07/lomboks-sneaky-throws.html
Or without magic: http://www.mail-archive.com/javaposse#googlegroups.com/msg05984.html
I have a couple of classes following the "Template Method" pattern. Abstract class A, and concrete extensions, B and C. Like this:
public abstract class A
{
protected abstract String getData() throws SomeException;
public void doWork() throws OtherException
{
try
{
// business logic ...
String data = this.getData();
// more business logic ...
}
catch(SomeException e)
{
log("...", e);
throw new OtherException("...", e);
}
}
}
public Class B extends A
{
protected String getData() throws SomeException
{
// complicated logic relying on lots of dependencies
}
}
public Class C extends A
{
protected String getData() throws SomeException
{
// different but equally complicated logic relying on lots of dependencies
}
}
I want to write a test to verify when getData() throws SomeException that OtherException is thrown. I really want to avoid mocking up all of the complicated dependencies that would be required to force getData() to throw. I don't care how getData() throws, I just want it to throw. So I think a partial mock is what I want. This is what I have:
import static org.easymock.EasyMock.*;
....
#Test(expected = OtherException.class)
public void testSomethingOrAnother() throws Exception
{
B target = createMockBuilder(B.class).addMockedMethod("getData").createMock();
expect(target.getData()).andThrow(SomeException.class).once();
replay(target)
try
{
target.doWork(); // expect this to throw OtherException;
}
finally
{
verify(target);
}
}
The test looks good to me, but when I run it I get this:
java.lang.Exception: Unexpected exception, expected<OtherException> but was<java.lang.RuntimeException>
... deleted for brevity ...
Caused by: java.lang.RuntimeException: Ambiguous name: More than one method are named getData
at org.easymock.internal.ReflectionUtils.findMethod(ReflectionUtils.java:96)
at org.easymock.internal.ReflectionUtils.findMethod(ReflectionUtils.java:64)
at org.easymock.internal.MockBuilder.addMockedMethod(MockBuilder.java:73)
at org.easymock.internal.MockBuilder.addMockedMethods(MockBuilder.java:92)
at com.mycompany.more.packages.BTest(BTest.java:83)
... deleted for brevity ...
... 16 more
To be clear: There is NOT an overload of the getData() method anywhere in the hierarchy.
Is EasyMock able to do what I'm trying to do here? What am I missing?
relevant versions numbers:
EasyMock 3.0
JUnit 4.4
Java 1.6
I think your problem may be the use of the addMockedMethod(String). Not sure why EasyMock is complaining about an ambiguous method name if there are no overloads. But the following worked for me:
#Test
public void testSomethingOrAnother() {
B target = null;
try {
target = EasyMock.createMockBuilder(B.class).addMockedMethod(B.class.getDeclaredMethod("getData")).createMock();
EasyMock.expect(target.getData()).andThrow(new SomeException());
EasyMock.replay(target);
} catch (NoSuchMethodException e) {
fail(e.getMessage());
} catch (SomeException e) {
fail(e.getMessage());
}
try {
target.doWork();
fail("doWork should have thrown an exception");
} catch (OtherException e) {
//pass
}
}
With Easymock:3.2 you are able to specify types of the parameters of the method. Take a look IMockBuilder#addMockedMethod(String methodName,Class<?>... parameterTypes)
Thanks.
I was wondering if it was possible to write a method to throw an exception and have another method catch these exceptions.
For example,
public static void checkCircle() {
try {
checkPixel(a);
checkPixel(b);
checkPixel(c);
} catch (MyException e) {
System.out.println("not circle");
}
private static void checkPixel(anything) {
if (img.getRGB(xValue, yValue) != pOrigColour) {
throw new MyException();
}
}
class MyException extends Exception {
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
Thing is I want to the checkPixel method to throw a MyException, indicating that there is no circle, regardless of the results of the other calls.
Yes, it is possible. In your method declaration, you can add a throws clause, which indicates that your method might throw an exception.
In your case, you should modify your method declaration like this:
private static void checkPixel(anything) throws MyException {
// ...
}
You should note that exceptions should be used for... exceptional situations. Using them for simple error handling is highly unconventional, and adds unnecessary burden on users of your classes. In your case, you might want to add a boolean hasCircleAtLocation () method that would return true if there is a circle at the provided location.