3

I have a lot of code that calls static methods on Foo like "Foo.method()". I have two different implementations of Foo and would like to use one or the other depending on the circumstances. In psuedocode:

File Foo1.java

class Foo1 implements Foo {
  public static int method() {
    return 0;
  }
} 

File Foo2.java

class Foo2 implements Foo {
  public static int method() {
    return 1;
  }
}

File Main.java

if(shouldLoadFoo1()) {
  Foo = loadClass("Foo1");
} else {
  Foo = loadClass("Foo2");
}

Is this possible with Java metaprogramming? I can't quite wrap my head around all the dynamic class loading documentation. If not, what's the best way to do what I'm trying to do?

Steve
  • 31
  • 2
  • Why is it necessary to load the classes dynamically and not just create separate instances of Foo1 and Foo2? – Tom Ravenscroft Feb 14 '11 at 23:35
  • I guess his problem here is that Foo1 and Foo2's methods are static. That's a bit strange and having different implementations for the same operation usually means he shouldn't use static methods, but it's hard to judge for this generic example. Steve, what do these classes actually do? If their contracts are the same, why aren't you using interfaces? – PaoloVictor Feb 14 '11 at 23:42
  • Are you sure `method()` is static? It doesn't look that way from the code provided. – CurtainDog Feb 14 '11 at 23:54
  • @Hunter2: Let's say that Foo is a logging class. Foo1 logs to disk and Foo2 logs to the network. There is one static method "log". – Steve Feb 14 '11 at 23:58
  • @Steve, classes in java are matched by reference but they're resolved by name. – bestsss Feb 15 '11 at 00:56

6 Answers6

3

Essentially you have two classes with the same interface but different implementations,Wouldn't it be better to do it using an interface?

in your main class, depending on the circumstances you would construct your class with the appropriate instance.

FooInterface foo;
MainClass (FooInteface foo, other fields) {
   this.foo = foo;
}


....

then just use foo from them on.

Another way is to use AspectJ, define a point cut on every Foo.method call, in in the advice for the point cut have your if (shouldLoadFoo1()) { Foo1.method()} etc ..

hhafez
  • 38,949
  • 39
  • 113
  • 143
  • The problem is that there is a lot of legacy code that uses Foo and I cannot change the APIs for all that code to pass in a FooInterface. I could change all the "Foo.method()"s to "Foo.getRealFoo().method()" or something like that but I'm trying to hide the fact that there are multiple Foo implementations since it isn't important to the code calling Foo. – Steve Feb 14 '11 at 23:44
  • 2
    Honestly this is the cleanest way, you can make a judgement on how much effort it would require to re factor vs the benefit. – hhafez Feb 14 '11 at 23:48
  • Have a look at my answer Re AspectJ to weave this logic throughout your code, while still separating it from your code. – hhafez Feb 14 '11 at 23:50
  • 1
    Gotta back up the comment on refactoring being the cleanest way. In my experience, it will quickly start to be more work to hack around this than it will be to fix it. What the original author did was write the code _so that you cannot_ do what you want to do. If that wasn't their intent, well, they're not a good programmer. If that _was_ their intent, you just honestly need to put in the work to fix it, as the world has changed since then. Start with your new FooInterface instead of Foo2, and slowly write out the old Foo where you need the new functionality. – Matt Feb 15 '11 at 01:02
2

You can use a factory pattern to do this.

static Foo makeMeAFoo()
{
  final Foo foo;
  if(shouldLoadFoo1()) {
    foo = new Foo1();
  } else {
    foo = new Foo2();
  }
  return foo;
}

Which is I think what you're asking for. Though I like hhafez' suggestion better myself.

(Note my answer is now OBE b/c the questioner shifted the methods to be static rather than instance methods. Nevertheless, the tone of other answerers is good... solving this problem by explicit classloading just because you want to select specific static methods is a kludge.)

andersoj
  • 22,406
  • 7
  • 62
  • 73
2

What you have written doesn't make sense from a linguistic standpoint. Foo is an type, and a type is not a variable and cannot appear on the LHS of an assignment. You cannot treat a type as a value in Java ... the language doesn't allow it.

The closest that you can get to what you are trying to do is something like this:

Class fooClass;
if (loadFoo1) {
    fooClass = Class.forName("some.pkg.Foo1");
} else {
    fooClass = Class.forName("some.pkg.Foo2");
}
Foo foo = (Foo) fooClass.newInstance();  // using the no-args constructor

(I've left out the exception handling ...)

Note that fooClass will be an instance of the class Class which provides runtime handles that are used for performing operations reflectively. We are NOT actually assigning a type. We are assigning an object that "denotes" a type ... in a limited fashion.


HOWEVER ... if you don't need to use dynamic loading you should not use it. In other words, if the underlying problem that you are trying to solve is creating instances of classes that could be statically loaded, then it is better to use the factory pattern; see @andersoj's answer for example.


UPDATE

I just figured out what you are probably trying to do here. That is, you are trying to figure out a way to choose between different static methods (i.e. Foo1.method() and Foo2.method()) without explicitly naming the classes at the point where the call is made.

Again, what you are trying to do simply won't work in Java:

  • You cannot declare a static method in an interface.
  • You cannot call a static method in an implementation class via the interface.
  • Static method calls are not "dispatched" in Java. They are bound statically.

There is a way to do something roughly like this using reflection; e.g.

Class fooClass;

// Load one or other of the classes as above.

Method m = fooClass.getDeclaredMethod("method");
Integer res = (Integer) m.invoke(null);

(As before, I've left out the exception handling)

Once again you would be much better off doing this without resorting to dynamic loading and reflection. The simple approach would be to create a helper method like this in some utilities class:

public static int method() {
    return useFoo1 ? Foo1.method() : Foo2.method();
}

Better still, do it the OO way: declare method in the Foo interface as a instance method, create a singleton or an injected instance of Foo1 or Foo2, and rely on polymorphism.

But the take away is that there is NO WAY to avoid changing all of the places in your codebase where method() is called ... if you want to be able to choose between Foo1.method and Foo2.method at runtime.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I know it doesn't make sense, I said it was psuedocode. I believe it conveys the idea I am trying to get across which is why I used it. – Steve Feb 14 '11 at 23:49
  • @Steve - the problem is that it **doesn't** convey what you are trying to do. It is unclear whether you are trying to load a class or creating an instance. – Stephen C Feb 14 '11 at 23:52
  • I'm trying to load a class, I don't need to instantiate the classes. – Steve Feb 14 '11 at 23:54
  • 1
    Well, see my answer then. That's the closest you are likely to get in Java. – Stephen C Feb 14 '11 at 23:56
  • Agreed with Stephen C here -- Why load a class if your goal is not to instantiate it? There are use cases, but they are usually pretty clearly defined and tightly focused (and usually unpalatable). You might consider beefing up your question to make it clear what the use case is, for the benefit of future readers. – andersoj Feb 15 '11 at 00:09
2

The typical approach to exchanging implementations is to use a non-static method and polymorphism, typically using dependency injection to tell the depedent code the implementation to use.

The next cleanest way is the singleton pattern, i.e. to declare:

public abstract class Foo {
    protected abstract void doSomeMethod();

    // populated at startup using whatever logic you desire
    public static Foo instance; 

    public static void someMethod() {
        instance.doSomeMethod();
    }
}

The really hacky way to solve your problem would be what you ask for, i.e. to have two different class files for the same class, and decide at runtime which one to use. To do that, you would seperate your project into 4 different jar files:

  • loader.jar that determines the classpath to use and constructs the classloader for the actual application. The classes in loader.jar must not reference Foo.
  • foo1.jar that contains one implementation for Foo
  • foo2.jar that contains another implementation for Foo
  • common.jar that contains everything else

Loader.jar would then contain a bootstrap method like:

void bootstrap() {
    URL commonUrl = // path to common.jar
    URL fooUrl;
    if (shouldUseFoo1()) {
        fooUrl = // path to Foo1.jar
    } else {
        fooUrl = // path fo Foo2.jar
    }
    URL[] urls = {fooUrl, commonUrl};
    ClassLoader loader = new UrlClassLoader(urls);
    Class<?> mainClass = loader.loadClass("my.main");
    mainClass.newInstance(); // start the app by invoking a constructor
}
meriton
  • 68,356
  • 14
  • 108
  • 175
2

I am not sure I fully understand the problem here (I see many has that issue), but let me try to help. If your problem was coming down just to using appropriate function method(), you could create a utility function that depending on an instance of a given class will call appropriate method, e.g.

private static int getResultOfFoo(Foo foo)
{
int res = -1;
if(foo instanceof Foo1)
    res = Foo1.method();
else res = Foo2.method();
return res;
}

Otherwise, I agree with Stephen C: "Well, see my answer then. That's the closest you are likely to get in Java."

Boro
  • 7,913
  • 4
  • 43
  • 85
1

In your example you in fact have not two different versions of class Foo, but two different implementations of the interface Foo, which is fine in most cases. (They even can exist parallel to each other.)

It is possible to load multiple classes of the same name, but they have to be loaded by different classloaders. This also means that you can't have a third class referencing it by name and then using one or the other (without the third class also being on two classloaders).

Sometimes it may be sensible to have different versions of a class (with same external interface) for different configurations where it would be used (such as "on client side" / "on server side", when some common class in both modules depends on it), and in rare cases you would have both modules in the same VM at the same time - but in most cases it would be better to use the "one interface and multiple implementing classes" approach instead.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210