Case simplified:
Legacy code. 3 exceptions X,Y and ZException. Class A has method public C fetch(...) throws X and YException and class B has public C fetch(...) throws ZException. The method implementation is almost the same, so I was wondering could I refactor it to some helper class. Method signatures cannot be changed. I came up with the following helper class
public class Common<T extends Exception, V extends Exception>{
public static interface ExceptionSupplier<T extends Exception> {
public T create();
}
private ExceptionSupplier<T> es;
private ExceptionSupplier<V> es2;
public Common(ExceptionSupplier<T> es, ExceptionSupplier<V> es2) {
this.es = es;
this.es2 = es2;
}
public void method() throws T, V {
//example that would could throw both T and V
if (Math.random() < 0.5) {
throw es.create();
} else {
throw es2.create();
}
}
}
Then I can create instance of that Common class in A and B e.g.
helper = new CommonThrower<ZException, ZException>(zSupplier, zSupplier);
helper = new CommonThrower<XException, YException>(xSupplier, ySupplier)
and call helper.fetch(...) and it shows (in eclipse) beging throw correct types. However it will (as somewhat expected) throwing ZException twice.
My question is there any problem having a method signature throws SomeException, SomeException (i.e. declaring the same exception again)? The code compiles and runs fine.
... throws IOException, IOException { ... .
Redundancy is just confusing, but runs fine.
A more common example of redundancy is something like
throws StreamCorruptedException, IOException {
In this case StreamCorruptedException is an IOException so it is not needed, but might be included to make it consistent with the Javadoc which might give more details for this exception.
Related
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.
Assume we have two classes:
Class A:
import java.io.IOException;
public class A {
public void test() throws IOException{
System.out.println("test in A");
}
}
Class B:
import java.io.IOException;
public class B extends A {
#Override
public void test() throws Exception{
System.out.println("test in B");
}
}
This gives a compiler error, and I would like to know the reason for it. I can get the answer by myself, but this is not fully scientific, but partly logically.
I wrote a blog post in Azerbaijani. When I wrote the blog I was stuck in the loading process.
Please be careful in quotes:
I think that when the compiler reads the B class, it loads the method headers of A and method headers of B. And when you call test of A, the JVM calls the test of A, but as body calls test of B, and at that time we will have this method:
public void test() throws IOException{ // <-- Header of A
System.out.println("test in B"); // <-- Body of B
// Here I can throw wide Exception from IOException
// because here is the body of the test in B. The test
// method in B can throw Exception so the compiler
// doesn't approve of this version of the code.
}
Is the process really going on as what I wrote above?
Loading headers issue I was stuck exactly here.
How does the linking process work? I can't figure out background of
A a = new B(); // When the compiler converts this line into bytecode
// does it loads of method headers of A and method
// body's of B
a.test()
calls the test of the B class. I know logically, but I can't figure out at the compiler level, linking process.
Imaigine you have the following code:
A a = new B();
try {
a.test();
} catch (IOExceoption e) {
//do some specific handle for IOExceoption
}
Now imaigine what happens if b.test() throw an Exception which is NOT IOException? nobody will handle it, and that breaks java's checked exceptions mechanism.
The other way around however, is perfectly fine:
public class A {
public void test() throws Exception {
System.out.println("test in A");
}
}
public class B extends A {
#Override
public void test() throws IOException{
System.out.println("test in B");
}
}
A a = new B();
try {
a.test();
} catch (Exception e) {
//handle
}
Now, note that the catch handles a general Exception, including the specific IOException, and the code will compile perfectly.
The overidden method can throw anything which is an IOException your case. Or more generally, it can throw any exception that is a ParentException. So, you can throw FileNotFoundException since it is also an IOException.
Why?
Not adhereing to this rule breaks the primary contract between parent-child in inheritance.
Somewhere else in your code you can safely assume that the exception thrown from the call to test() will always be IOException irrespective of Which implementation of A threw it. If you were allowed to throw InterruptedException in your case, what exception should the caller catch?
The compiler doesn't load anything. It just marks the method as invoke virtual which means this method is overriden and supposed to be called at run-time based on the object type
From a bit different angle, you are basically not overriding existing method in super type.
Exceptions which are thrown are part of the method's signature.
When you declare your test method in class B with override annotation, you are in fact trying to override (or implement) method which does not exist in its parent A.
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
package com.rnd.core.java;
import java.io.IOException;
public class TestExceptionInheritance {
public void checkExcpetions () throws ArrayIndexOutOfBoundsException{
System.out.println("Inside TestExceptionInheritance ParentClass");
throw new ArrayIndexOutOfBoundsException();
}
}
package com.rnd.core.java;
import javax.sound.midi.MidiUnavailableException;
public class TestExceptionInheritance2 extends TestExceptionInheritance {
public void checkException () throws MidiUnavailableException {
System.out.println("Hello");
throw new MidiUnavailableException();
}
#Override
public void checkExcpetions() throws StringIndexOutOfBoundsException {
// TODO Auto-generated method stub
//super.checkExcpetions();
System.out.println("HI");
}
public static void main(String[] args) throws Exception {
TestExceptionInheritance obj = new TestExceptionInheritance2();
obj.checkExcpetions();
}
}
I have overriden the checkException Method of the parent class in my subclass but I throw a different exception here.
I want to understand why the compiler allows me to throw an altogether different exception; though I know that the method version would be decided based on the reference type.
-------------------Edit 1---------------------------
I have added an #override notation over the overridden method. Overridden method allows me to throw StringIndexOutOfBoundException and RunTimeException along with ArrayIndexOutOfBoundException but not any other exception like for example Exception.
According to the Exception class hierarchy, both StringIndexOutOfBoundException and ArrayIndexOutOfBoundException are subclasses of IndexOutOfBoundException.
How and why does the compiler allows me to throw StringIndexOutOfBoundException because ArrayIndexOutOfBoundException will be never be caught in StringIndexOutOfBoundException.
Thanks for your help.
The real simple answer is you are not overriding what you think you are. The parent class declares a function public void checkExcpetions () and you have a function public void checkException (). These are two different functions which is why there is no compiler error
Using the #Override tag is one way to have the compiler check that you are overriding what you think you are. In this case if you used the tag there would be an error since you are not overriding a parent method
Inside Method A, there is method B. Method B throws exception, but method A compiled even it does not catch exception or throws exception, could it be possible?
Method B is something like as below:
MethodB() throws SomeException{
if(Some)
doneSomething
return
else if(some)
donesomethingElse
return
throw SomeException
}
If the SomeException extends RuntimeException. Then you don't need to catch it even if the signature looks like that.
Also note that you can just remove the throws SomeException in that case. Read more here.
Yes, there are some unchecked exception, who might not be caught / rethrown.
Look at this tutorial - Unchecked Exceptions.
Even if SomeException is a checked exception, this can happen due to separate compilation.
Suppose you write a class:
public class B {
public static void foo() {
}
}
Then a class that calls it:
public class A {
public static void main(String[] args) {
B.foo();
}
}
Then say:
javac A.java
java A
All is fine. Now change B:
public class B {
public static void foo() throws java.io.IOException {
throw new java.io.IOException();
}
}
And this time just compile B before running:
javac B.java
java A
You get:
Exception in thread "main" java.io.IOException
at B.foo(B.java:4)
at A.main(A.java:4)
In the real world this happens when individual .jar files are updated after they've been modified by maintainers who don't understand the problems caused by adding more throws clauses.