Java Type Signatures

by Mithrandir

This year, I had to do several assignments in Java. Some of them didn’t require me to use a specific IDE but there was one arguing that we should use Eclipse because it helps us in development better than any other tool.

And it does so, with several exceptions. My main disappointment came from the way it handles function signatures. You have the @Override annotation which may help you spot mistakes in overwriting functions from base classes. It helps you doing this but it also fails at some point.

Consider that you have the following interface

public interface ITest { 
    public int function1(int x); 
    public int function2(int x); 
    public int function4(int x); 
    public int function5(int x) throws CustomException; 
}

with the following CustomException definition.

public class CustomException extends Exception { 
 
    String msg; 
 
    public CustomException(String msg){ 
        this.msg = msg; 
    } 
 
    @Override
    public String toString(){ 
        return msg; 
    } 
} 

From this, you would understand that the only exception which could be raised is CustomException from function5. Is this really true?

Let’s see an abstrac class using this interface

public abstract class ATest1 implements ITest { 
 
    @Override
    public int function1(int x) { 
        if (x == 0) 
            throw new InvalidParameterException("X must not be 0"); 
        return x; 
    } 
 
    @Override
    public abstract int function2(int x); 
 
    @Override
    public int function4(int x) { 
        return x; 
    } 
 
    @Override
    public int function5(int x) throws CustomException { 
        if (x == 0) 
            throw new CustomException("X must not be 0"); 
        return -x; 
    } 
 
} 

Now, we have one more exception which can be thrown but Eclipse didn’t suggest to add a throws declaration to that method.

Now, consider the following two classes, one of them defining the single left out method

public class Test2 extends ATest1 { 
 
    @Override
    public int function2(int x) { 
        return x + 10; 
    } 
} 

while the other overwrites a few other methods and doesn’t throw any exception in them.

public class Test1 extends ATest1 { 
 
    @Override
    public int function2(int x) { 
        return x + 1; 
    } 
 
    @Override
    public int function1(int x){ 
        return x + 1; 
    } 
 
    @Override
    public int function5(int x){ 
        return x + 100; 
    } 
 

} 

Suppose now, that the user has access only to the interface, he knows only the signatures of functions. In this case, what will the following program do?

    public static void main(String args[]){ 
        ITest t1 = new Test1(); 
        ITest t2 = new Test2(); 
        Test2 t3 = new Test2(); 
        Test1 t4 = new Test1(); 
 
        System.out.println(t1.function1(0)); 
        System.out.println(t1.function2(0)); 
        System.out.println(t1.function4(0)); 
        try{ 
            System.out.println(t1.function5(0)); 
        } catch (CustomException e) { 
            e.printStackTrace(); 
        } 
 
        System.out.println(t2.function1(0)); 
        System.out.println(t2.function2(0)); 
        System.out.println(t2.function4(0)); 
        try { 
            System.out.println(t2.function5(0)); 
        } catch (CustomException e) { 
            e.printStackTrace(); 
        } 
 
        System.out.println(t3.function1(0)); 
        System.out.println(t3.function2(0)); 
        System.out.println(t3.function4(0)); 
        try { 
            System.out.println(t3.function5(0)); 
        } catch (CustomException e) { 
            e.printStackTrace(); 
        } 
 
        System.out.println(t4.function1(0)); 
        System.out.println(t4.function2(0)); 
        System.out.println(t4.function4(0)); 
        System.out.println(t4.function5(0)); 
    } 

Each try-catch block above was suggested by Eclipse. As you see in function5 calls, this is not a symmetrical way, giving rise to some errors while refactoring. Hopefully, Eclipse is there and suggests you that there are some exceptions.

Yet, should you make main throw exceptions instead of reporting them, there would be no way to reason why that exception appeared there after changing t4 with t3 in the last line.

However, the above main doesn’t throw CustomException. It throws InvalidParameterException from t2.function1(0), something which is inexplicable from the function’s signature.

This is similar to getting a NullPointerException somewhere where you expected only valid references. Or a segfault in a library function which didn’t present itself as being able to segfault. Something which you cannot fix and the documentation doesn’t explain how it was produced. A bug and a very nasty one.

You may say that this is an unlikely situation, that since you knew what exceptions are thrown, you can add the throws annotation to the interface and then you can document it. However, there are cases of big projects with multiple inheritance paths and lengthy development history with multiple teams of developers. In these instances, it is very common to throw some exception in a method from a class deep down in the hierarchy without needing to report it upwards and document it.

In Haskell, it is simpler. When you change a function’s type to include the possibility of failure, the Maybe you might add must be put in every signature for each function calling you function, you’ll have to change them all. Thus, you’ll surely be forced to document the possibility of failure.

Only head: empty list and Prelude: undefined or exceptions regarding missing patterns can show up (maybe several others but I guess that they can be sorted into one of these three). The last two cases indicate a serious error in the library design and can be fixed, someway or the other. And the first case is the reason why a stack-trace printing debugger for Haskell is so much desired.

But this was a rant about Java, not about Haskell.

EDIT: I hit the post button to early and it is possible that a version with TODOs has appeared in your feed, I’m sorry for that.

About these ads