how to load a .class file dynamically?
Hello World ;)
Does anyone know, how I can create an object of a class, that was compiled during the runtime?
The facts:
- The user puts a grammar in. Saved to file. ANTLR generates Scanner and Parser (Java Code .java)
- I compile these file, so XYScanner.class and XYParser.class are available.
- Now I want to create an object of XYScanner (the classnames are not fixed, but I know the filename). I tried to call the constructor of XYScanner (using reflection) but nothing works and now I am really despaired!
Isn't there any way to instantiate the class in a way like
Class c = Class.forName("/home/mark/XYScanner");
c scan = new c("input.txt");
The normal call would be:
XYLexer lex =new XYLexer(new ANTLRFileStream("input.txt"));
The problem using reflection is now that the parameter is a ANTLRFileStream, not only an Integer, as in this example shown:
Class cls = Class.forName("method2");
Class partypes[] =new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod("add", partypes);
method2 methobj =new method2();
Object arglist[] =new Object[2];
arglist[0] =new Integer(37);
arglist[1] =new Integer(47);
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer)retobj;
System.out.println(retval.intValue());
Has anyone an idea? Thanks for each comment in advance!!!
I am not sure whether i understood your question. If XYScanner is the filename then it would be the class name. Am i right?anyway check this URL http://forum.java.sun.com/thread.jspa?threadID=765440&messageID=4366305
Yes, you are right. The generated file has the name 'XYScanner.java' and the classname is XYScanner. But I do not know the class name now, because the XY depends of the grammar name by the input of the user.I will try the link now - thank you!!!
you just need to load the class with a classloader.this can all be done programmatically. http://www.google.com/search?hl=en&q=java+load+classif you make good use of interfaces you may not have to use reflection.
i am hungry for those 10 duke stars.
ClassLoader cl = ClassLoader.getSystemClassLoader();
String classfile = "SomeClass.java";
Class _class = cl.loadClass(classfile);
Object obj = _class.newInstance();
[some interface] _if = ([some interface])obj;
of course if this file is not in your classpath then you will have to
jump through some hurdles (overriding classloader's loadclass
method for example).
Thanks TuringPest!
Your code works with the Scanner, but not with the Parser. Could it be that there are some problems because of an inner class (XYParser$DFA27.class) in XYParser? I get an Exception in thread "main" java.lang.InstantiationException: XYParser
at java.lang.Class.newInstance0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at Run.main(Run.java:46)
when I call newInstance(); here:
ClassLoader cl2 = ClassLoader.getSystemClassLoader();
String classfile2 = "XYParser";
Class _class2 = cl2.loadClass(classfile2);
Object obj2 = _class2.newInstance();
Parser _if2 = (Parser)obj2;
CommonTokenStream tokens = new CommonTokenStream(_if);
Parser g = new Parser(tokens);
where Parser is an interface for XYParser.
Btw: I deleted the '.java' in the String classfile.
Don't know if it is relevant, but does your XYParser have a (implicit or explicit) default constructor? Or maybe the inner class doesn't have one...
From Javadoc:
InstantiationException - if this Class represents an abstract class, an interface, an array class, a primitive type, or void; or if the class has no nullary constructor; or if the instantiation fails for some other reason.
If you need a constructor with arguments check out
XYParser.class.getConstructor(someClass.class).newInstance(someInstanceOfSomeClass);
Can't you solve it without reflection?
Hi.
The XYParser class as well as the Parser interface for XYParser has a constructor with one parameter (CommonTokenStream). The inner class has 'no' constructor. Do I have to instantiate this inner class too? I don't need an object of the inner class to call the parser, just the parser probably needs it to proceed.
I tried to solve this problem without reflection: works for the scanner. But in order to start parsing, I have to call a method named with the start-non-terminal symbol of the grammar :( So I think I cannot use an interface to call this method with maybe changing name, isn't it ?
You can't use Class.newInstance() if you have no no-args constructor in the class you've loaded, rather you need to find the constructor. This is why most plugin systems use a no-args constructor and some kind of init method to set parameters.
Don't worry about inner classes, the main class should take care of their needs once that's instanciated.
And be aware of URLClassLoader, you'll probably want to put your dynamically generated class on a separate repository, not on the class path.
What you'll need to do will be something like this:
File repositoryBase = new File("my/dynamicclasses");
ClassLoader cl = new URLClassLoader(new URL[]{repositoryBase.toUrl()});
Class<? extends ParserInterface> clazz =
cl.loadClass(className).asSubclass(ParserInterface.class);
Constructor<? extends ParserInterface> constructor = clazz.getConstructor(
new Class[]{CommonTokenStream.class});
ParserInterface parser = constructor.newInstance(tokenStream);
I don't understand what you mean by 'the Parser interface has a constructor' since interfaces cannot contain constructors. I guess you're not talking about the language construct Interface but rather a superclass?
You don't have to create an instance of the inner class. Create the outer class by using
CommonTokenStream tokens = new CommonTokenStream(_if);
ClassLoader loader = ClassLoader.getSystemClassLoader();
String classfile2 = "XYParser";
Class _class2 = loader.loadClass(classfile2);
Constructor constructor = _class2.getConstructor(CommonTokenStream.class);
Parser parser = (Parser)constructor.newInstance(tokens);
Since you are dealing with runtime defined classes I think you cannot go without reflection. You don't have the classes at compile time... I don't know ANTLR, so this might be a suggestion that doesn't make sense, but isn't it possible to not generate java files, but write your own class structure for the compiler you want and use the API of ANTLR create instances of it with correct values?
Message was edited by:
Peetzore
Dump all of your class files into a directory.
Use the File class to list all files and iterate over the files.
Use a URLClassloader to load the URL that is obtained for each file with file.toURI().toURL() (file.toURL() is depricated in 1.6)
Load all of the classes in this directory, that way any anonymous classes are loaded as well.
If you have already loaded the class, it won't try to load it again.
From this point you should be able to just get a class with Class.forName(name)
> Dump all of your class files into a directory.
> Use the File class to list all files and iterate over
> the files.
Nasty
> Use a URLClassloader to load the URL that is obtained
> for each file with file.toURI().toURL() (file.toURL()
> is depricated in 1.6)
Wrong, the URL you give it must be that of the directory, not the class file.
> Load all of the classes in this directory, that way
> any anonymous classes are loaded as well.
Anonymous classes automatically loaded when the real one is
> From this point you should be able to just get a
> class with Class.forName(name)
No. Class.forName uses the classloader of the class it's called in, which will be the system classloader. It won't find classes loaded by a URLClassLoader.
> > Dump all of your class files into a directory.
> > Use the File class to list all files and iterate
> over
> > the files.
>
> Nasty
Not really, when you are dynamically creating code, I would expect the output to be centralized, not spread out all over the place.
>
> > Use a URLClassloader to load the URL that is
> obtained
> > for each file with file.toURI().toURL()
> (file.toURL()
> > is depricated in 1.6)
>
> Wrong, the URL you give it must be that of the
> directory, not the class file.
No, I did this quite recently, you can give it a specific class file to load.
>
> > Load all of the classes in this directory, that
> way
> > any anonymous classes are loaded as well.
>
> Anonymous classes automatically loaded when the real
> one is
It can't load it if it doesn't know where the code is. Since you haven't used a URL classloader to load once class file at a time, you would never encounter this. But if you loaded a class file that had an anonymous classfile it needed, would it know where to look for it?
>
> > From this point you should be able to just get a
> > class with Class.forName(name)
>
> No. Class.forName uses the classloader of the class
> it's called in, which will be the system classloader.
> It won't find classes loaded by a URLClassLoader.
got me there.
