2

I'm trying to make a program based off of the Pebble Smartwatch Operating System. I have a text file called PebbleOS_Faces.txt which has all the names of the different watch faces I will have on the watch.

I use PrintWriter to convert the content into a .java file, and then I compile it using the code I got from this post. It gets compiled into the build/classes/pebbleos folder (I'm using Netbeans for Mac, in case that makes a difference), next to all of the other classes. I've confirmed that this all works properly.

What's left is for me to actually run the class from inside the program. The thing is, each class would have a different name based off of the names of the watch faces in the PebbleOS_Faces.txt file. How do I create an instance of a class that has a variable name?

Community
  • 1
  • 1
Avery Vine
  • 154
  • 10

2 Answers2

0

You'll want to use Reflection. You can get a Class object by name using Class.forName(String). You can call the constructor with Class.getConstructors()[0].newInstance(<insert constructor arguments here>)*. Make sure all the classes inherit a common superclass so that you can do something like:

WatchFace face = Class.forName(watchFaceName).getConstructors()[0].newInstance();

*This is assuming there is only one constructor

Piper McCorkle
  • 1,044
  • 13
  • 27
0

I've never tried this with dynamically generated/compiled classes, but...

Try using Class.forName("com.example.YourClassName") to get a reference to the Class:

http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#forName(java.lang.String)

and then use Class.newInstance() to create an instance of the class:

http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#newInstance()

For this to work, com.example.YourClassName would have to be visible to your class loader.

For example:

Class clazz = Class.forName("com.example.YourClassName");
Object instance = clazz.newInstance();

The newInstance() call will only work if your class has a no-argument constructor.

If the YourClassName class's constructor requires arguments you must use a slightly different technique to call a specific constructor and pass values into it. For example, if you had a constructor like this:

YourClassName(Integer someInt, String someString)

Then you could do this to instantiate YourClassName via that constructor:

Class clazz = Class.forName("com.example.YourClassName");
Constructor constructor = clazz.getConstructor(Integer.class, String.class);
Object instance = constructor.newInstance(anInteger, "aString");

It would probably help if every instance of YourClassName implemented the same interface or extended the same base class so that you could cast the return value of newInstance() to that interface or base class. Then you would be able to call the interface or base class's methods on the instance. Otherwise, your only way to call useful methods on the Object would be to use reflection.

Firefly
  • 5,425
  • 1
  • 10
  • 10
  • After following your suggestions, I'm getting the following error: Exception in thread "main" java.lang.ClassNotFoundException: PebbleOS_Default, where Default is the name of the Watch Face. Is the class possibly in the wrong directory? – Avery Vine Jun 19 '15 at 18:09
  • Possibly. What is the package name of the PebbleOS_Default class? – Firefly Jun 19 '15 at 18:12
  • The package name is pebbleos, which is the same package name as the rest of the files. – Avery Vine Jun 19 '15 at 18:13
  • Ah, in that case I'd make sure that the class file is located at build/classes/pebbleos/PebbleOS_Default.class, and double check that /path/to/build/classes is on the classpath. This looks like an interesting list of things to check: https://myarch.com/classnotfound/ (though a lot of the suggestions there seem to be targeted toward web applications and may not help you) – Firefly Jun 19 '15 at 18:17
  • You could also use prunge's answer here http://stackoverflow.com/questions/7590495/method-to-dump-classes-on-the-classpath-from-inside-jvm to print your classpath at runtime and use that to confirm that build/classes is on the classpath. – Firefly Jun 19 '15 at 18:20
  • The class file is indeed located there, but (stupid question) how do I check what is on the classpath? – Avery Vine Jun 19 '15 at 18:24
  • This might be the easiest way. String classpath = System.getProperty("java.class.path"); for (String path : classpath.split(File.pathSeparator)) { System.out.println(path); } – Firefly Jun 19 '15 at 18:26
  • So /build/classes is on the class path, I'll put it here for reference. /Users/*********/Library/Mobile Documents/com~apple~CloudDocs/***** ****/ /Code/Home/Java/PebbleOS/build/classes – Avery Vine Jun 19 '15 at 18:32
  • Hmm... is the fully qualified class name (i.e. including the package name) specified when you call forName()? Try Class.forName("pebbleos.PebbleOS_Default"); – Firefly Jun 19 '15 at 18:42
  • Yes, thank you! That was exactly what I needed. Now to figure out how to use the methods within the class... thanks :) – Avery Vine Jun 19 '15 at 18:48
  • Woohoo! I'm glad to hear it, I was running out of ideas :) – Firefly Jun 19 '15 at 18:54
  • One parting tip on the methods: If you can get by with having the same public method signatures on every PebbleOS_* class, then using a common interface or base class across all your PebbleOS_* classes would be best. You can then cast the return value of newInstance() to that base class / interface and call all the methods therein. On the other hand, if every PebbleOS_* class needs its own unique/different method signatures (for example, on Default you need foo(int a, int b) and on MyAwesomeFace12 you need bar(int a)) then you could use method reflection to find and call those methods. – Firefly Jun 19 '15 at 19:00