The project I am working on is an API to support two different platforms. At runtime only one of the two platforms will actually be available on the classpath.
For the most part, I have pretty easily been able to write code like this that works fine
if(isPlatformOne(){
PlatformOne.doSomething();
}
Even if PlatformOne
does not exist at runtime, the check beforehand means the code does not run and no error will be thrown. This technique works for the VAST majority of situations however there is one case that I have run into where an error is thrown.
If PlatformOne
also implements a nonexistent interface AND that is used with a parameter that ALSO does not exist, then a NoClassDefFoundError
is thrown immediately when the containing class is loaded, regardless of whether the code actually executes or not.
Here's an example:
Interface:
public interface DeleteInterface {
void test(DeleteInterface delete);
}
Class:
public class DeleteClass implements DeleteInterface {
@Override
public void test(DeleteInterface delete) {
}
}
Main:
public class Test {
private final boolean test; //Cannot be constant or compiler will erase unreachable code
public Test() {
test = false;
}
public static void main(String[] args) {
if (new Test().test) {
DeleteClass c = new DeleteClass();
c.test(c);
}
System.out.println("SUCCESS!");
}
}
Deleting DeleteClass
and DeleteInterface
from the jar produces the following error at runtime:
A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/kmecpp/test/DeleteInterface
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: com.kmecpp.test.DeleteInterface
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
Why is an error thrown only for this specific case, and what's the best way to work around it without access to any of the target platforms' code?