Calling from Java to Lisp requires the Java class com.lispworks.LispCalls, and Java code that uses methods from this class. Currently all methods are static. com.lispworks.LispCalls is supplied by LispWorks in the file
7-1-0-0/etc/lispcalls.jar, except on Android where it is part of the
7-1-0-0/etc/lispworks.jar file. After Java is initialized, either by an explicit call to init-java-interface or implicitly by the system (for example on Android), you can check whether the Java to Lisp calls are possible (the class
LispCalls is available) by using check-lisp-calls-initialized.
There are two mechanisms for calling from Java to Lisp: direct calls and using proxies. Direct calls means calling directly a Lisp function from Java, passing the name of the symbol to funcall and the arguments. Using proxies meaning creating proxies from Lisp, and then passing such Lisp proxies to places where the interface(s) that it implements are required. Invoking a method on such proxy ultimately calls a Lisp function.
Direct calls are simple to use, and if you have a simple Java/Lisp interface can be all that you need. The proxies are needed when you use somebody else's interface, for example implement callbacks to user interaction in Android. They are also useful even if you write the Java side too to make a cleaner interface on the Java side, which is easier to switch between different implementations.
Note that on the Lisp side you will need to keep the Lisp symbol when delivering, most conveniently by
hcl:deliver-keep-symbols (see the
LispWorks Delivery User Guide
), and the name of the symbol is not interpreted using
See com.lispworks.LispCalls for full details.
Using proxies allows you to create from inside Lisp a Java proxy which implements one or more Java interfaces. The proxy can then be used whenever an object that implements any of the interfaces is required. When a method is applied to a proxy, it ultimately calls a Lisp function.
Defining a proxy is done normally at load time by define-lisp-proxy. It is possible to define a proxy at run time using setup-lisp-proxy. For example, defining a proxy that implements the
onTouchListener interface, specifying that when the method
"onTouch" is invoked it causes the function
text-view-on-touch-callback to be called:
Note: the result of make-lisp-proxy or make-lisp-proxy-with-overrides is "local", which means that it cannot be used outside the dynamic scope of the call to Lisp from Java in which it was created. If it is created outside the scope of a call from Java to Lisp, it must be used only in the thread that it was created.
When defining a proxy, you do not need to specify all the methods. You can specify a default function, which is called for any method for which you did not specify a function. See for example the proxy
lisp-othello-server-lazy in this example, which does not specify any method, and instead specifies a default function that handles all of them:
When defining a proxy, it is also possible to specify that the Lisp functions should be called with an extra argument user-data, which is associated with each specific proxy by passing
:user-data to make-lisp-proxy or make-lisp-proxy-with-overrides. This allows you to link each proxy with some of your data. If you do not specify this option, the functions in the proxy need to use the arguments and global data to decide what to do.
It is also possible to "override" the Lisp function at run time, which means specifying that when a Lisp function for a method should be invoked, another function is invoked instead. Overriding is specified by passing either of
:overrides-plist to make-lisp-proxy, or by using make-lisp-proxy-with-overrides. The main advantage of overriding is that it allows you to use run time closures, while the proxy definition itself allows only symbols. Overrides are efficient and are simple to use. For example, with the definition above, you can override the callback by:
Note that this example could easily be done using
:user-data instead, but that will have to be specified "statically" in the proxy definition, while overriding can all done dynamically when creating individual proxies.
To make it easier to detect typing errors in specifying the interface names and method names or specifying a Lisp function, the functions verify-lisp-proxies and verify-lisp-proxy are provided to verify all proxies or only one, respectively. Verification checks that all the specified functions are actually defined, and optionally also that all the methods that are declared in the interfaces are defined. The latter check must be done with Java running. You will typically use it when starting the application to check that all the proxies are OK, at least during the development phase.
The Lisp functions of the proxy are ordinary Lisp, but they need to return the correct value, unless the method has
Void as its return type. Returning the wrong value will call the java-to-lisp-debugger-hook (see init-java-interface) with an appropriate condition, and then return zero of the correct type (that is 0, 0d0, 0f0, Java
false, or Java
null) from the method call.
In some cases the method needs to throw some exception. The function throw-an-exception can be used to throw an exception from inside a call to a proxy function.
LispWorks User Guide and Reference Manual - 20 Sep 2017