Author Topic: Java: Creating an instance of a class by string name  (Read 2842 times)

Mike

  • Jackass In Charge
  • Posts: 11257
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Java: Creating an instance of a class by string name
« on: July 07, 2008, 02:00:44 PM »
Ok not sure if this is the best way of doing it but...

I'm creating a program that will basically have little programs in them.  I have a JFrame with a JPanel displaying the main menu.  After choosing the program and pushing the button it executes a function that creates an instance of the specific class and turns control over to it.  The specific classes extend JPanel and implements a given interface.  In there run function they hide the main menu and add themselves to the JFrame.  Once it is done it removes itself and turns the main menu back on.

Now what I want to do is create a resource file that lists all of the classes and their labels.  What I would like to do is be able to do something like:

Code: [Select]
String name = programSelector.getSelectedItem();
MyInterface prog = new name();

Anyone have an idea if this is possible or not?

Dumah

  • Jackass IV
  • Posts: 960
  • Karma: +21/-6
Re: Java: Creating an instance of a class by string name
« Reply #1 on: July 07, 2008, 02:05:51 PM »
Try the abstract factory pattern

I dont use Java, so I'll use C++ methods:
Create a global class (maybe a singleton) with a std::map as a member. At initialisation, populate the map with a string describing the class and a generator function (ie one that creates an object and returns a base interface).

When you want an object, give the global object the string...it looks up the entry and uses the generator to create the object passing back a base interface.

Cool pattern as it's pretty extensible - want more objects, just create the new class and change the initialization code

Mike

  • Jackass In Charge
  • Posts: 11257
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Java: Creating an instance of a class by string name
« Reply #2 on: July 07, 2008, 02:15:11 PM »
Ok I got it working in a test environment:
Code: [Select]
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package testproject;

/**
 *
 * @author s6163
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
throws ClassNotFoundException,
InstantiationException,
IllegalAccessException
{
String name = "testproject." + "Foo";
Main m = new Main();
ClassLoader loader = m.getClass().getClassLoader();
loader.loadClass(name).newInstance();
        // TODO code application logic here
    }

}

Now to put it in the actual program.

hans

  • Guitar Addict
  • Jackass In Charge
  • Posts: 3523
  • Karma: +46/-18
Re: Java: Creating an instance of a class by string name
« Reply #3 on: July 07, 2008, 02:36:33 PM »
Perhaps looking at Groovy or a dynamic runtime language would be good?

Do you know all the classes that will be executed ahead of time? Interfaces and Spring configuration might be another option.
This signature intentionally left blank.

Dumah

  • Jackass IV
  • Posts: 960
  • Karma: +21/-6
Re: Java: Creating an instance of a class by string name
« Reply #4 on: July 07, 2008, 02:48:21 PM »
Dont know about Java...looks like this might be built into the framework. Anyway, this is what I was talking about if you wanted to roll your own

Code: [Select]
#include <iostream>
#include <map>
#include <string>

class BaseInterface
{
public:
    virtual void Doohicky() = 0;
};

class DerivedOne : public BaseInterface
{
public:
    void Doohicky(){ std::cout << "DerivedOne does something..." << std::endl;}
};

class DerivedTwo : public BaseInterface
{
public:
    void Doohicky(){ std::cout << "DerivedTwo does something..." << std::endl;}
};

BaseInterface* CreateDerivedOne(){return new DerivedOne();}

BaseInterface* CreateDerivedTwo(){return new DerivedTwo();}

class Factory
{
    typedef BaseInterface* (*FUNC)();
    std::map <std::string, FUNC> m_map;
public:
    void Populate(const std::string& str, FUNC func)
    {
        m_map.insert(std::make_pair<std::string, FUNC>(str, func));
    }
    BaseInterface* Get(const std::string& str){return (*m_map[str])();}
}MyFactory;


int main()
{
    std::string Obj1Name = "DerivedOne";
    std::string Obj2Name = "DerivedTwo";
    MyFactory.Populate(Obj1Name, CreateDerivedOne);
    MyFactory.Populate(Obj2Name, CreateDerivedTwo);
    BaseInterface* Obj1 = MyFactory.Get(Obj1Name);
    BaseInterface* Obj2 = MyFactory.Get(Obj2Name);
    Obj1->Doohicky();
    Obj2->Doohicky();
    delete Obj1;
    delete Obj2;
}

Mike

  • Jackass In Charge
  • Posts: 11257
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Java: Creating an instance of a class by string name
« Reply #5 on: July 07, 2008, 02:49:55 PM »
Code: [Select]
[quote author=tgm link=topic=6885.msg68360#msg68360 date=1215455793]
Perhaps looking at Groovy or a dynamic runtime language would be good?
[/quote]
Well I'm trying to cheat and use java for its ease of GUI.

[quote]
Do you know all the classes that will be executed ahead of time? Interfaces and Spring configuration might be another option.

[/quote]
All classes will be known at the beginning of runtime.  Each class will also be implementing an interface.  Right now it looks like:
@Action
public void launchProgram()
{
DrainageProgram prog = null;
String name = "drainage." + (String) programSelector.getSelectedItem();

try
{
ClassLoader loader = this.getClass().getClassLoader();
prog = (DrainageProgram) loader.loadClass(name).newInstance();
}
catch (ClassNotFoundException e)
{
System.out.println("Class not found: " + e.getMessage());
}
catch (InstantiationException e)
{
System.out.println("Instantiation: " + e.getMessage());
}
catch (IllegalAccessException e)
{
System.out.println("Illegal access: " + e.getMessage());
}

if (prog != null)
{
prog.setFrame(DrainageApp.getApplication().getMainFrame(), mainPanel);
prog.run();
}
I need to think about the error handling some more.  Probably some type of popup box.

That actually worked.


My main reason for doing this is pure laziness.  If they want to add another program I want to be able to implement it and add an entry into one file.  I don't want to have to remember to change data in a bunch of spots.

webwhy

  • Jackass IV
  • Posts: 608
  • Karma: +15/-10
Re: Java: Creating an instance of a class by string name
« Reply #6 on: July 07, 2008, 04:41:24 PM »
If you know all the classes @ runtime, and can load all of those classes using the class loader that loaded the executing class, You can simply store the classes in a data structure by referencing the Class object via the "class" property.  You simplify the exception handling a bit, and the class is guaranteed to be initialized properly, while loading via a class loader does not.  I've always been taught to instantiate classes via Class.forName() or the class property only using class loaders explicitly if I need to load a class not available to the current class loader.

This example uses generics so it requires at least a Java 5 environment

Code: [Select]
package webwhy;

import java.util.HashMap;
import java.util.Map;

public class Test implements Printer {

private String member = "test 1 class";

public Test() {}

public Test(String member) {
this.member = member;
}

public void print() {
System.out.println(member);
}

public static void main(String[] args) {
Map<String, Class<? extends Printer>> classes = new HashMap<String, Class<? extends Printer>>(2);
classes.put("Test App", Test.class);
classes.put("Test 2 App", Test2.class);

try {
classes.get("Test App").newInstance().print();
classes.get("Test 2 App").newInstance().print();
}
catch(InstantiationException e) {
e.printStackTrace();
}
catch(IllegalAccessException e) {
e.printStackTrace();
}
}
}

class Test2 extends Test {

public Test2() {
super("test 2 class");
}
}

interface Printer {
void print();
}

Mike

  • Jackass In Charge
  • Posts: 11257
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Java: Creating an instance of a class by string name
« Reply #7 on: July 07, 2008, 05:02:39 PM »
From your example it would appear that if I wanted to add say test 3 I would have to modify
Code: [Select]
public static void main(String[] args) {
Map<String, Class<? extends Printer>> classes = new HashMap<String, Class<? extends Printer>>(2);
classes.put("Test App", Test.class);
classes.put("Test 2 App", Test2.class);
which I was trying to avoid.

The funny part is now that I have meet with the engineer this just became irrelevant as I won't need to go the route of having multiple "sub programs".

webwhy

  • Jackass IV
  • Posts: 608
  • Karma: +15/-10
Re: Java: Creating an instance of a class by string name
« Reply #8 on: July 07, 2008, 05:17:00 PM »
From your example it would appear that if I wanted to add say test 3 I would have to modify
Code: [Select]
public static void main(String[] args) {
Map<String, Class<? extends Printer>> classes = new HashMap<String, Class<? extends Printer>>(2);
classes.put("Test App", Test.class);
classes.put("Test 2 App", Test2.class);
which I was trying to avoid.

The funny part is now that I have meet with the engineer this just became irrelevant as I won't need to go the route of having multiple "sub programs".

Yeah...that' s true.  I read this in your response to tgm
Quote
All classes will be known at the beginning of runtime
so I assumed that all possible classes were known at runtime, but after looking at your example more carefully, i understand what you were trying to say now.