Search code examples
reflectionclasspathjavanimbus

Java: How do I override a method of a class dynamically (class is eventually NOT in classpath)?


How do I call a method of a class dynamically + conditionally?
(Class is eventually not in classpath)

Let's say, I need the class NimbusLookAndFeel, but on some systems it's not available (i.e. OpenJDK-6).

So I must be able to:

  • Get to know it that class is available (at runtime),
  • If it's not the case, skip the whole thing.
  • How do I manage to override a method of a dynamically-loaded class
    (thus creating an anonymous inner sub-class of it)?

Code example

public static void setNimbusUI(final IMethod<UIDefaults> method)
    throws UnsupportedLookAndFeelException {

  // NimbusLookAndFeel may be now available
  UIManager.setLookAndFeel(new NimbusLookAndFeel() {

    @Override
    public UIDefaults getDefaults() {
      UIDefaults ret = super.getDefaults();
      method.perform(ret);
      return ret;
    }

  });
}

EDIT:
Now I edited my code, as it was suggested, to intercept NoClassDefFoundError using try-catch. It fails. I don't know, if it's OpenJDK's fault. I get InvocationTargetException, caused by NoClassDefFoundError. Funny, that I can't catch InvocationTargetException: It's thrown anyway.

EDIT2::
Cause found: I was wrapping SwingUtilities.invokeAndWait(...) around the tested method, and that very invokeAndWait call throws NoClassDefFoundError when loading Nimbus fails.

EDIT3::
Can anyone please clarify where NoClassDefFoundError can occur at all? Because it seems that it's always the calling method, not the actual method which uses the non-existing class.


Solution

  • Get to know it that class is available (at runtime)
    Put the usage in a try block ...

    If it's not the case, skip the whole thing
    ... and leave the catch block empty (code smell?!).

    How do I manage to override a method of a dynamically-loaded class
    Just do it and make sure the compile-time dependency is satisfied. You are mixing things up here. Overriding takes place at compile time while class loading is a runtime thing.

    For completeness, every class you write is dynamically loaded by the runtime environment when it is required.

    So your code may look like:

    public static void setNimbusUI(final IMethod<UIDefaults> method)
        throws UnsupportedLookAndFeelException {
    
        try {
            // NimbusLookAndFeel may be now available
            UIManager.setLookAndFeel(new NimbusLookAndFeel() {
    
                @Override
                public UIDefaults getDefaults() {
                    final UIDefaults defaults = super.getDefaults();
                    method.perform(defaults);
                    return defaults;
                }
    
            });
       } catch (NoClassDefFoundError e) {
           throw new UnsupportedLookAndFeelException(e);
       }
    }