0

Let's say I have a labyrinth with AI characters, where the users define the characters. Each user provide the classes for their individual characters. All the characters/classes extend some class/type C which has method control().

I want to do call each user's control() method, but I don't know how many users there will be or what classes they will provide. How do I resolve this problem?

EDIT: I wanted to convey that I do not know how many subclasses there are, or what their names are. Therefore, I am not able to place those subclasses in the code statically.

EDIT 2: Is there a way of doing this WITHOUT using reflection? I am aware that reflection solves the problem, but I hoped there was a cleaner implementation.

EDIT 3: It completely necessary to have the users create the different classes, as the point of the program is to test competing AIs.

btw, I am writing this in Java.

Daniel Kats
  • 5,141
  • 15
  • 65
  • 102
  • Which step is causing the problem? Loading classes dynamically? – biziclop Mar 13 '12 at 19:51
  • Well, this is the basis of polymorphism. You will just call the inherited control() method and the correct control() method of the sub-type objects will be called automatically. – Cagil Seker Mar 13 '12 at 19:54
  • If I have an object of type A, then it will call A.control(). If I have an interface, I am not able to lump the similar objects together. – Daniel Kats Mar 13 '12 at 19:59
  • @BlackSheep I'm not sure any way to meet your requirement of not using reflection. Since you have no idea how many classes will be created, or what they will be called, how could you possibly avoid dynamic location and loading of the code? – dlev Mar 13 '12 at 20:04
  • How do you want to have a dynamic type loading without reflection? That is plain impossible. – GETah Mar 13 '12 at 20:06
  • @GETah I understand that it makes the problem more difficult. I guess in particular I was looking for something like Python's eval command. I guess that's not possible in a strongly-typed language like Java... – Daniel Kats Mar 13 '12 at 20:08
  • @dlev And if I knew what they were called? If I could get a list of names of the classes? – Daniel Kats Mar 13 '12 at 20:09
  • @BlackSheep Do you know the names of all the classes at compile-time? If you don't know the names, or there are classes you don't know about, then you will need some kind of dynamic location and loading. In Java, that is provided by reflection. – dlev Mar 13 '12 at 20:24

3 Answers3

2

The whole thing about inheritance is that you don't need to know the exact type. If you have a reference to an object that is of type C or a subclass of C, you can call your "control()" method on them and it will call the right method, i.e. the one implemented by the child class.

Not knowing how many users means you'll have to use a list or something and loop over it.

public class AIGame {
public static void main(String[] args) {
    List<AICharacter> characters = new ArrayList<AICharacter>();
    characters.add( new ReallySmartAICharacter() );
    characters.add( new ReallyDumbAICharacter() );
    for ( AICharacter c : characters ) {
        c.control();
    }
    }
}

interface AICharacter {
    public void control();
}

class ReallySmartAICharacter implements AICharacter {
    @Override
    public void control() {
        // TODO do something clever here        
    }   
}

class ReallyDumbAICharacter implements AICharacter {
    @Override
    public void control() {
        // TODO do something stupid here        
    }
}
Jochen
  • 2,277
  • 15
  • 22
  • If I have an abstract class then it would be list, which is invalid. If it is not an abstract class, then cannot have a list with multiple types. – Daniel Kats Mar 13 '12 at 19:57
  • There is nothing invalid about an *object* of type List. If you want to instantiate such an object you must use a concrete implementation of List, for example ArrayList. That Player is abstract does not matter at all. Even though you might want to change your type declaration to List extends Player> to make clear that you allow subtypes, even though that should not be necessary. – Jochen Mar 13 '12 at 20:04
  • As long as your multiple types have a common root, and that root is part of the list declaration, there is not problem. It does not matter whether this root is an interface, an abstract class or an actual class. – Jochen Mar 13 '12 at 20:06
  • Can you provide an example and post it as an answer? – Daniel Kats Mar 13 '12 at 20:11
  • Edited my original answer. You'd of course have to create the instances through reflection. – Jochen Mar 14 '12 at 16:16
  • How would I create the instances through reflection? I would need to replace "new ReallySmartAICharacter()" and "new ReallyDumbAICharacter()"... – Daniel Kats Mar 19 '12 at 19:48
  • Search SO for examples. http://stackoverflow.com/questions/5637487/java-properly-checked-class-instantiation-using-reflection – Jochen Mar 19 '12 at 21:51
2

First of all, you need to decide if the different characters' behavior is really going to be as differentiated as to need Java code to implement the particular behaviors. Perhaps the behavior can be expressed with a single class and only modified by setting different values for parameters such as speed, health, attack strength etc. In this case you would get rid of the inheritance problem altogether and use a single class while users would only provide different configurations.

Now, if you really need very custom behavior and load custom Java classes, I see two main solutions.

First is the standard one. It uses just a tiny bit of reflection. You define an interface, for example:

public interface C {
    void control(); //Params skipped for brevity
}

Now, your users create classes which implement this interface. The only problem is how to create an instance of the player's class. Once you have it, you call its control() or other methods via the interface. First, users need to make this class loadable. Thiscan be done through the network or in other complex ways but the simplest is that they put their .class or .jar file in their classpath when they run your application. Now all you need is to create an instance of the class. Assuming you specify the requirement that the class have a zero-argument constructor (you can define a method in your interface to load some configuration and perform initialization later on), you would be doing something like:

C gameCharacter = (C)Class.forName("your.fully.qualified.ClassName").newInstance();

Apart from error handling, that's all the reflection you need. You can now call all methods of interface C on your gameCharacter object - without knowing who or how wrote it and what exactly the methods do.

The other solution would be to use Groovy or another similar language to compile and run code on the fly. In this case you don't need the custom JAR in the classpath and you can even get around the need to know the name of the class to be loaded. Your user can provide the Java code of control() method in the form of text, and you can have a stub class whose control() method only compiles and executes the Groovy code the user provided. This may be more convenient, but requires the custom character code to be provided to you as source code, not compiled JAR, which may be a problem for some users. Also, this solution is more convenient if the implementations are going to be short and self-contained while the separate JAR and loading via reflection is better if the loaded code is more complex, uses helper classes apart from the main class etc.

Michał Kosmulski
  • 9,855
  • 1
  • 32
  • 51
  • Your posted code causes the following Exception: java.lang.InstantiationException. According to the documentation, it's because the parent class is an abstract class or an interface. – Daniel Kats Mar 19 '12 at 19:47
  • @BlackSheep Perhaps your class doesn't have a constructor without arguments? In that case you need to get a `Constructor` object using `Class.forName("your.fully.qualified.ClassName").getConstructor(Class...)` and then call its `newInstance(Object...)` method, passing any arguments the constructor needs. – Michał Kosmulski Mar 19 '12 at 20:15
0

If all the characters extend some common class, for convenience let's call it Character, then you can use polymorphism to dynamically call each of the control() methods.

In other words, if each subclass of Character overrides control(), then all you need to do is call it normally and Java will figure out which control() method to call.

e.g.

Character[] characters = new Character[2];
characters[0] = new Man(); // Man is a subclass of Character
characters[1] = new Woman(); // same with Woman

character[0].control(); // <- this will call the control() method as defined in Man

The mechanism for this is called late (or dynamic) binding, which you can read more about here: http://en.wikipedia.org/wiki/Late_binding

If the subclasses are not known at compile-time (i.e. they are specified at run-time), then you will need to use reflection to load them.

To keep track of each user, use a dynamically sized List type like a LinkedList or ArrayList. This way you don't need to know how many users there are beforehand.

tskuzzy
  • 35,812
  • 14
  • 73
  • 140
  • The problem with this approach is that I do not know the subclass names, or how many subclasses there are. – Daniel Kats Mar 13 '12 at 19:58
  • You can address that using reflection as I have mentioned. – tskuzzy Mar 13 '12 at 19:58
  • Sorry, I should have initially specified that I wanted to avoid reflection. I edited my question to reflect this. – Daniel Kats Mar 13 '12 at 20:01
  • Well the whole point of reflection is to solve problems like this. I doubt it's possible to do without it. – tskuzzy Mar 13 '12 at 20:13
  • Right, but what you specified has no reflection. Can you post a code sample that would dynamically insert "new Man()" and "new Woman()" in your code with reflection? – Daniel Kats Mar 19 '12 at 19:50