Search code examples
javalambdadeferred

Is there a way to call a deferred implementation in Java?


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?


Solution

  • 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 new 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.