Loading and Reloading a Java Class from Matlab

Paul Milenkovic

Copyright 2004

Matlab has a facility for creating instances of Java classes and for invoking methods on those instances. Examples are given by D. Spielman. There is interest in having an interactive environment for selectively creating instances and invoking methods of Java classes (see A. Wosh), and Matlab seems to provide this. Unfortunately, quoting from the Mathworks web site regarding restrictions on unloading Java classes

“If you load a Java class into MATLAB and, sometime later, modify and recompile the class, you must exit and restart MATLAB in order to use the updated class definition. This restriction exists because the Java VM does not provide a way to unload a Java class once it has been loaded. Exiting and restarting MATLAB terminates and restarts the Java VM, which then allows you to load the updated class.”

No combination of import or clear commands from the Matlab environment works because this restriction is a characteristic of the Java VM, that creating the first object instance of a class loads that class and once a class is loaded it stays loaded. The reasons why Java has to be that way are discussed on the SUN web site.

Java has a facility called a class loader which may allow reloading a compiled class according to a pattern described by Mitchell and Foote. Sample programs on how to use a class loader for the purpose are given by J. Zukowski.

The Java runtime has a default class loader employed when creating an object instance with the new operator. Any object created in that fashion along with the class loaded to create that object go in the namespace of that default class loader, and that namespace is immutable – you can add classes to that namespace but you cannot take them away or reload them. Any new class loader that you create with a ClassLoader object establishes a new name space into which you can load classes and create objects. That new name space is also immutable, but every time you create a new ClassLoader object instance, you create a fresh namespace that allows loading a recompiled class and seeing the changes. You can simply overwrite or set to null references to old ClassLoader object instances to let them be garbage collected, but you don't need to force garbage collection because each new ClassLoader object starts from fresh.

Objects created with classes loaded by the new class loader can only be accessed using the Java reflection API – you can't get at them in the manner of objects created by the new operator because they are in a namespace not accessible by the regular means. The reflection API is the way of inspecting objects and invoking methods on those objects at runtime. This shouldn't be a problem because Matlab invokes methods on Java objects using reflection anyway. But it turns out that for whatever reason, Matlab will not recognize instances of type Object created in a new class loader namespace as valid Java objects. Such an object can be accessed in Java without reflection by having an interface or base class in the default namespace and by casting the class loader-created object to that interface or base class. With Matlab, that doesn't work either. What does work is to create a proxy object in the default namespace to return to Matlab, and to have the proxy object call the corresponding methods of the target object that lives in the class loader namespace. See Cornell and Horstmann for more details on proxies.

Class Proxy allows creating and configuring a proxy object at runtime. The source codes to implement this are here. The ingredients are a class called ClassLoad to create an object instance inside the namespace of a new class loader instance as well as create the proxy object to allow Matlab to see that object, a public interface class called InPlace defining the methods Matlab is able to invoke on that object, and a class called SineFilter that implements that interface. ClassLoad does all this at runtime so it does not need to be changed if you substitute a new interface and a new target class for InPlace and SineFilter – InPlace and SineFilter may be given different names. InPlace defines an interface which is not reloadable without exiting and restarting Matlab, but once the interface is defined, class SineFilter can be recompiled and the changes will be visible from a Matlab session.

If this system is used in a classroom setting, ClassLoad is supplied by the instructor and does not change, InPlace can be supplied by the instructor or by the student to specify an interface, and it can only change between Matlab session, and SineFilter can be supplied by the student as part of completing an assignment.

There are also some restrictions on directories where files can go. My setup puts each student account on a virtual network drive F: with F:\matlab the default work directory for student Matlab projects. The files ClassLoad.java and InPlace.java can go in that directory, but the file SineFilter.java cannot go there if I want to be able to reload that class. F:\matlab is on the default Java classpath, and any classes found there will be loaded with the default class loader, defeating what we are trying to accomplish. I put SineFilter.java in F:\matlab\myjava. When compiling it, I need to tell it where class InPlace is found, so from F:\matlab\myjava I invoke

javac -classpath .. SineFilter.java

which tells the Java compiler to look for the next directory up (.. same as F:\matlab) to find InPlace.class.

From the Matlab command window, one may invoke

>> cl = ClassLoad('F:\\matlab\\myjava/')

to create a reloadable object from a class found in F:\matlab\myjava. Note the peculiar syntax (required by Java) for specifying this directory. Creating an object instance is as simple as

>>sinegen = cl.NewInstance('SineFilter')

I can create the first 4 samples of a cosine wave of the default frequency of 100 Hz, peak value 1, and phase 0 for a sampling frequency of 22.05 kHz by

>>sinegen.Init(1.0/22.05)

>>sinegen.Apply([0.0, 0.0, 0.0, 0.0])

The Apply() method takes an input array and adds the sine wave to that array – that is why I supply a 4-element 0-valued input array to see 4 samples of the cosine function.

I can edit SineFilter.java (say, to change the default magnitude from 1.0 to 2.0), recompile it, repeat

>> sinegen = cl.NewInstance('SineFilter)

>>sinegen.Init(1.0/22.05)

>>sinegen.Apply([0.0, 0.0, 0.0, 0.0])

to see the output at twice the amplitude. By the way, to produce a 2000 Hz sine wave with default amplitude and phase I can invoke

>>sinegen.SetParams([2000.0]);

>>sinegen.Init(1.0/22.05)

>>sinegen.Apply([0.0, 0.0, 0.0, 0.0])

(SetParams() needs to precede Init()), or to produce a 440 Hz sine wave of amplitude 1.5

>>sinegen.SetParams([440.0,1.5]);

>>sinegen.Init(1.0/22.05)

>>sinegen.Apply([0.0, 0.0, 0.0, 0.0])