No Exception thrown at calling a non implemented method from a dynamically loaded/instantiated class/object

Viewed 30

Today I stumbled upon an interesting behaviour I would not have expected at all: If you load a class dynamically via URLClassLoader, get an instance of that class, cast this instance to an interface(which the loaded class implements, but not in the version the loading class knows) and call an unimplemented method from the interface, there is no exception thrown whatsoever.

For example: you write a class XYZ implementing an interface. At the time of writing(and later compiling) that class, there was a 'fun()' method to implement from the interface. After compiling, you package a jar containing the class(but not the interface). Then you edit the interface and add a 'otherFun()' method header. Calling the 'otherFun()' method in classes with reference to the versions of the interface with the added method header on the dynamically loaded and instantiated class will be valid compiler wise but of course there is an exception at runtime.... or is there? Turns out, that there is no exception raised at this point. The code just stops executing and jumps straight into the finllay block.

Why so? And what is happening for real at this point?

Here is some code for better understanding:

Interface at packaging the jar:

interface MyInterface{
    public void fun();
}

Interface after packaging the jar:

interface MyInterface{
    public void fun();
    public void otherFun():
}

loading code:

try{
    File jarFile = new File(jarPath);    //the jar at this path holds a class with the name '$name', which implements the interface 'MyInterface' in the version without the otherFun() method
    URLClassLoader child = new URLClassLoader(
        new URL[] {jarFile.toURI().toURL()},
        this.getClass().getClassLoader()
    );
    Class<?> rawClass = Class.forName(name, true, child);
    Class<? extends MyInterface> explicitClass = (Class<? extends MyInterface>)rawClass;
    Constructor<? extends MyInterface> constructor = explicitClass.getConstructor();
    MyInterface instance = constructor.newInstance();
    instance.fun();    //works as intended
    instance.otherFun();    //does nothing, but jumps into the finally block
    System.out.println("I am the unreachable code block, due to the unimplemented method call above.");
}catch(Exceptione e){
    System.out.println("I am also an unreachable code block, due to the straight jump to the finally block.");
} finally {
    System.out.println("I'm done without raising an exception."):
}
1 Answers

As Tim Moore pointed out in the comment above, the triggered error is not an exception(inheriting java.lang.Exception) but an error(inheriting java.lang.Error). Therefore the try{...}catch(Exception e){...} is not working, but a try{...}catch(Throwable t){...} is working. Interesting side-note: the thrown error does not make it through to stderr! So if you don't explicitly catch a Throwable somewhere, you'll never see any error messsage on your screen. The application is just not working, without any output(feels like Windows :D )

Related