I'm not sure if the terminology is exactly correct here.
I have a class with some static methods that are called by several other classes in some library code written long ago.
The class with the static methods that are called by several other classes has a reference to a third party class that now has a version 2.
Is there a way I can code this class to do something like "when this static method is call, there will be something that you need to call to see which version of the library you should be using"?
Such that when I pull this library code in to a new project as a jar file, I can include something in each project that will tell my class in the library with the static methods to use a specific version.
Such that I don't need to provide a version1 and version2 of each of the classes in my library.
Am I on the right track with deferred?
A lambda function maybe?
CodePudding user response:
Sort of. Java doesn't load a class unless you actually need it. This means you can do something like this:
public String getFoo() {
switch (versionRequired()) {
case 1: return new Version1Impl().getFoo();
default: return enw Version2Impl().getFoo();
}
}
boolean versionRequired() {
// figure out which version is needed here.
// if you're having trouble with this, you can use `Class.forName` to check
// if a certain type is even available. Worst case scenario, you can
// ask a class for its own bytecode and hash that. This is very tricky.
return 1;
}
private static class Version1Impl {
public String getFoo() {
return new UseSomethingFromV1().getFoo();
}
}
The clue with the weird 'redirect to an inner class' stuff here is that the inner class won't be loaded until the switch branch is taken, and as long as e.g. Version2Impl
never loads, you also never get the error that it is referring to a class that doesn't exist (because it was designed for v2 of that library, but only v1 is available).
NB: About that version detection scheme: The problem with hashing classes is that a class may not have changed between 2 major releases (you wanted it to be different but it will not be). Or, on the flip side, a minor point release update may have changed it (it is now different, but you did not want it to be). One solution is to hash a lot of classes, which helps with the first case (you want to notice changes), but makes the second worse. You can do this with e.g. try (InputStream in = String.class.getResourceAsStream("String.class")) {
- which works for any class. Toss this inputstream through a hasher (search the web if you don't know how, this is a very common task, plenty of tutorials to find).
Many libraries have a supported way to ask them what version they are, but there is no standard, so check the docs of this library.
CodePudding user response:
There are a few different solutions to your problem, depending on how your third-party library has changed.
I'm assuming version 2 of your third-party class has new methods that are not on version 1 that you need?
The simplest solution is to implement your class against version 2 of the library, but handling LinkageError (or more a more specific subclass, for instance NoSuchMethodError), and reverting to version 1 calls if these errors are caught. You can also handle if it's a completely different class, by handling NoClassDefFoundError.
Alternatively, if performance isn't critical, you can also use reflection.