简体   繁体   中英

Java: Dynamically Load Multiple Versions of Same Class

What I'd like to be able to do is to load set of classes, probably all in the same folder. All of which implement the same interface and are the same class, then in my code I'd like to be able to call functions on those classes.

Based on your answer to my question, it seems you want to define a game interface and then plug in any number of AI implementations, probably configured from a .properties file. This is fairly standard use of an API interface.

You define an EngineInterface providing a method that accepts game state and returns the move. Then you define multiple classes that all implement EngineInterface. Your driver reads a property file to get the names of the implementation classes, instantiates them with Class.forName() and stores them in a list and/or map. Then when the driver gets requests it invokes each implementation in turn and keeps track of the results.

Have you tried something like:

class Move;   // some data type that is able to represent the AI's move.

interface AI {

    Move getMove( GameState state);
};

AIOne implements AI;
AITwo implements AI;

Each class would implement its own algorithm for generating a move, but would be called but called by common method

It is possible to do what you want with OSGI but you could as well use a custom classloader. The idea is that you have to instanciate a classloader for every version of the class you want to load. Here you can find a good explanation.

But I think what you really need to solve your problem is something based on interfaces like described by Jim Garrison or Dave L Delaney...

  1. If you can use OSGI, its as simple as snapping a finger! In oSGI you can have multiple verssions of the same class. All you do is have same bundles with different versions.

  2. Otherwise you can still write your custom class loader that reads both the classes. One way of doing it would be like this. You write two ClassLoaders, one of them loads one version of the class and the other loads the other version of the class. Now based on the need you choose the classloader1 or classloader2 to load the class. So now you can also have multiple versions of the same class loaded simultaneously in the memory.

Note: Make sure this is actually you want to do, there may be other ways of coming around your problem.

The only framework I know which does support what you are after is OSGI :

替代文字

Its network model, described in this article " Exposing the boot classpath in OSGi ", does allow that

One of the side effects (or aims) of the networking model is type isolation or class versioning: multiple version of the same class can coexist nicely inside the same VM since each one is loaded into its own network, its own space.

See this tutorial for beginning and choose on eof the OSGI Framework (like Equinox , Knoplerfish or Apache Felix )

It can be done using dynamic class loading. It is not loading class of different version but different sub-classes of a super class or interface.

The important steps are:

(1) Use Class.forName(...) to load a class by name. The class must be in the class path.

(2) Use aClass.newInstance() to instantiate the object. This is easy if there is no parameter needed for the constructor.

The following code should provide some idea for you. It does not handle exception which you have to do it.

class Context {
    void moveUp();
    void moveDown();
    ...
}

interface AI {
    void action(Context con);
}

public class Game {
    public Game() {
        Context  aContext    = new Context();
        String[] aAIClsNames = this.getAIClassNames("ai.list");
        AI[]     aAIs        = this.loadAI(aAIClsNames);
        this.run(aAIs);
    }
    String[] getAIClassNames(String pAIClassListFile) {
        // .. Load the file containning the AI-class file names
    }
    AI[] loadAI(String[] pAIClsNames) {
        AI[] AIs = new AI[pAIClsNames.length];
        for(int i = 0; i < pAIClsNames.length; i++) {
            String    aAIClsName       = pAIClsNames[i];

            
            Class<? extends AI> aAICls = Class.forName(aAIClsName);

            
            AIs[i] = (AI)aAICls.newInstance();
        }
        return AIs;
    }
    void run(AI[] pAIs) {
        // ...
    }
}

Hope this helps.

Jim's response is good - you name the classes you want to use, and they all conform to a common API. However the solution given assumes the classes are all available on the classpath of the application already. You may be wanting to be able to add more implementations later, eg after the application is installed.

If thats the case, then you'll probably need to use a custom classloader. For example, you could allow people to put jar files inside a particular folder somewhere, and to add the class names of the implementations to a properties file. You would then need a custom classloader than can load classes from the jars inside that folder, and you would use that classloader to load the classes (eg using Class.forName(className, classLoader)).

In fact if you have a classloader per jar file, you will be able to have multiple classes with the same names across the jar files, as the classloader defines the class name boundaries. This is pretty much what OSGI is doing.

Here's some code relating to loading classes from jars:

http://sourceforge.net/projects/jcloader/ http://www.javaworld.com/javatips/jw-javatip70.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM