3

So I'm building a game engine and I need to be able to call methods from a class that implements a certain interface(I only want to call methods implemented by the interface).

My problem is that I don't know what the class name will be implementing it.

So how does, for instance, Java call the run() method in all classes that implement Runnable without knowing the class name?

splitgames
  • 1,669
  • 3
  • 13
  • 14
  • 2
    That's exactly what interfaces are for, I'm not really sure I understand your question since you're aware of interfaces... – Mat Sep 07 '13 at 09:51
  • As Mat says, that's the idea of using interfaces/inheritance.. If you allow a method to receive a Runnable object then you can call the run method. Then if you need to do something specific of the implemented class (but now you must know it) you can cast it to the child. – porfiriopartida Sep 07 '13 at 09:57
  • *"..without knowing its name?"* You mean the class, rather than the interface, right? – Andrew Thompson Sep 07 '13 at 10:00
  • YEs, I don't know the class name – splitgames Sep 07 '13 at 10:00
  • Do you have a least an object instance even if you don't know the class name ? – Julien Sep 07 '13 at 10:25
  • You can load all classes in a jar using code like github.com/HarishAtGitHub/doc/blob/master/loadAllClassesInAJar/…. for each unknown class you can gather info like what is the super class , what is the class which it implements (interface) and maintain them and use them whenever the interface methods need to be called .. – Harish Kayarohanam Feb 14 '14 at 05:12

4 Answers4

2

Really, you're asking about the Factory pattern or a dependency injection container such as Spring.

Of course you can call the methods on an interface, the question is how you get the instance. That of course has to be specified, coded or configured somewhere. Configuration is preferable if there could ever be more than one in the future.

Thus, more of a real example:

public interface MovementStrategy {
    public Move selectMove (Actor actor, ActorSituation theirSituation);
}

public class MonsterTypes {
    public static MonsterType GOBLIN = new MonsterType( "goblin", new AttackMover(1.2));
    public static MonsterType TROLL = new MonsterType( "troll", new AttackMover(0.45));
    public static MonsterType DEER = new MonsterType( "deer", new FleeMover(2.0));

    // useful to have, also.
    public static List<MonsterType> getAllRegisteredTypes(); 



    public static class MonsterType {
        protected String name;
        protected MovementStrategy moveStrategy;
        // TODO -- getters & setters for all properties.

        // constructor.
        public MonsterType (String name, MovementStrategy moveStrategy) {
            this.name = name;
            this.moveStrategy = moveStrategy;
        }
    }
}

public class AttackMover implements MovementStrategy {
    // SPEC: generally move towards/attack PC, with varying speeds.
}
public class FleeMover implements MovementStrategy {
    // SPEC: generally run away from PCs.
}

This isn't probably a perfect design -- it conflates "movement" (aka goal-seeking) with the actor's turn/actions overall -- but hopefully it gives you some more idea.

Thomas W
  • 13,940
  • 4
  • 58
  • 76
  • I have 2 questions : 1. How can you have arguments in the construcotr? 2. It doesn't let me initilaise the interface(MonsterType = new MonsterType()) – splitgames Sep 07 '13 at 10:10
  • MonsterType should be JavaBean-style, or with chained setters.. and should have a constructor. But this is intended to be concise/ outline, for the sake of example -- so it does not include all these. – Thomas W Sep 07 '13 at 10:21
  • If you designed `MonsterFactory` to load from XML or via Spring instead of hardcoded type constants, you could have the MonsterTypes load & register from config. In such a __configuration-based__ approach, the movement strategies (and other configurable strategies/or components) would ultimately be loaded by __reflection__. The Java classloader & Class.forName() would do the work, loading configured classes by name. – Thomas W Sep 07 '13 at 10:28
1

If you only want to call methods from the interface (good!), then you usually don't need to now the name of the implementor.

getRunnableFromSomewhere().run();

always works and calls the run() method on the instance that is returned by that method.

If you want to now the class name at runtime, simpy call getClass().getName() on the instance:

System.out.println(getRunnableFromSomewhere().getClass().getName());

A simple example with the Number interface:

public class NumberExample {
    public static void main(String[] args) {
        MagicNumber magic = MagicNumberProvider.get(); // a random implementation
        System.out.println(magic.getMagicNumber().doubleValue());  // We know nothing about the implementations
    }
}

class MagicNumberProvider {
    public static MagicNumber get() {
        return Math.random() > 0.5d ? new ItsMagicOne() : new ItsMagicTwo();
    }
}

interface MagicNumber {
    public Number getMagicNumber();
}

class ItsMagicOne implements MagicNumber {
    @Override
    public Number getMagicNumber() {return new Long(1);}
}

class ItsMagicTwo implements MagicNumber {
    @Override
    public Number getMagicNumber() {return new Double(2.5);}
}

It only calls interface methods and we have, from the perspective of the main method, no idea, which implementation of MagicNumber is used (it's random) and on which implementation of Number we actually call the doubleValue() method.

Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
  • I'm not sure I totaly understand the getRunnableFromSomwhere(), can you explain? – splitgames Sep 07 '13 at 09:57
  • This is a dummy method / example that returns just some `Runnable`, assume it has a signature like `public Runnable getRunnableFromSomewhere() { ... }` – Andreas Dolk Sep 07 '13 at 10:00
  • 1
    Yes but to instanciate a class that implements an interface you need the class name, wich i don't have – splitgames Sep 07 '13 at 10:02
  • I've added an example. Hope it's understandable now :) – Andreas Dolk Sep 07 '13 at 10:14
  • 1
    I don't think you understand, I don't know the names of the classes that implement the interface, in this code you obviously do. – splitgames Sep 07 '13 at 10:18
  • I understand your problem . But I have seen similar use cases in a number of plugin frameworks where they just ask us to implement an interface and at run time our class is being picked up. In nexus oss plugins they use google guice ,where they ask the plugin writers to prifix the class with @Named , and the guice framework will load the class automatically .https://github.com/sonatype/nexus-oss/blob/master/components/nexus-core/src/main/java/org/sonatype/nexus/security/filter/NexusSecurityFilterModule.java?source=cc – Harish Kayarohanam Feb 09 '14 at 11:22
1

Service Provide Interface

You can use java SPI (Service Provider Interface) by which later implementing jars declare the same service in the manifest. A using app can do a lookup, iterate over them and pick one.

An example is the different XML parser implementations.

Parameter

For your case it might suffice to have a run method:

class GameRunner {
    public static void mainEntry(MyGameInterface mgi) {
    }
}

And the implementors may do

cöass ThirdPartyGame implements MyGameInterface {
}

GameRunner.mainEntry(new ThirdPartyGame());

Plugin with java reflection

You can make your ad-hoc, self-define plugin emchanism, and use java reflection to instantiate the class. The third party jar must be placed at some location, that is in the class path, as defined in your jar's manifest. The class somewhere defined:

String klazz = resBundle.getProperty("pluginClass");
Class<MyGameInterface> klazz = Cass<MyGameInterface>.forName(klazz);
MyGameInterface game = klazz.getConstructor().newInstance();
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
0

If I understood your question correctly it seems you have slightly misunderstood polymorphism, you don't need to know the type that implements the interface.

See the following example, there is only one class that directly knows the types of each enemy, the initializing class.

  import java.util.ArrayList;
import java.util.List;

public class SO18671999 {

    public static interface Enemy {

        public void Attack(Enemy other);

        public String getName();

    }

    public static class Dragon implements Enemy {

        String name = "Onyxia";

        public void Attack(Enemy other) {
            System.out.println(this.name + " attacks " + other.getName()
                    + " for 10 dmg!");
        }

        public String getName() {
            return this.name;

        }
    }

    public static class Cerberus implements Enemy {

        private String name;
        private int dmg;

        public Cerberus(String name, int dmg) {
            this.name = name;
            this.dmg = dmg;
        }

        @Override
        public void Attack(Enemy other) {
            System.out.println(this.name + " attacks " + other.getName()
                    + " for " + this.dmg + " dmg!");
        }

        @Override
        public String getName() {
            return this.name;
        }

    }

    public static class EnemyInitializer {
        private List<Enemy> enemies;

        public EnemyInitializer() {
            enemies = new ArrayList<>();
            enemies.add(new Dragon());
            enemies.add(new Cerberus("CerberusHeadLeft", 10));
            enemies.add(new Cerberus("CerberusHeadRight", 10));
            enemies.add(new Cerberus("CerberusHeadCenter", 20));
        }

        public List<Enemy> getEnemies() {
            return enemies;
        }
    }

    public static class EnemyAttacker {
        private EnemyInitializer eI = new EnemyInitializer();

        public void startAttacking() {
            List<Enemy> enemies = eI.getEnemies();
            for (Enemy one : enemies) {
                for (Enemy two : enemies) {
                    if (one == two)
                        continue;
                    one.Attack(two);
                }
            }
        }
    }

    public static void main(String[] args) {
        EnemyAttacker eAttacker = new EnemyAttacker();
        eAttacker.startAttacking();
    }

}
arynaq
  • 6,710
  • 9
  • 44
  • 74
  • But the problems is that I don't have the names of the classes that implements the interface at any given point, so I can't initilize tham like that. – splitgames Sep 07 '13 at 10:15
  • Seems like a design problem then, I am sure there are (more advanced than I know of) ways of finding every class that implements an interface at runtime but it is far better for you to use an initializor and maybe read from a textfile or databas. – arynaq Sep 07 '13 at 10:21