In this paper, we use ABCL (Armed-Bear-Common Lisp), which is one of our Common Lisp processing systems and operates on the JVM, to operate classes and methods written in the Java language. The basic procedure is shown below.
** "Only API for Java is provided, but I want to use the application with Common Lisp (hereinafter referred to as CL)!" **. There is, right? Yeah, I'm sure there is.
** "But there are almost no Japanese documents, I'm not very good at Clojure, and it's hard to write other than Lisp!" ** I'm not sure if anyone is there, but this article is based on ABCL for those people. This is an article that describes the basics of the inside.
In this article, we will only call simple Java methods from CL. I will write about the details in a separate article, so if you are looking for it, you may not get anything, so browser back is recommended.
This was the most (or only) helpful.
At first, I used to refer to this, but I didn't really understand the meaning and was addicted to it.
If you get lost, it may be a good idea to hit the source code.
Information about ABCL is basically here, right?
Unfortunately, there is no document for those who can only read or cannot read Japanese. It is not written in such difficult English, so please do your best to read it.
I hope this article will give you an idea of the basics!
Now, I will explain the ABCL specifications for calling Java from Common Lisp.
Just in case, let's take a look at this execution environment. It should probably work on other platforms as well. I haven't verified it. .. ..
First, I will explain how to call a static method. The simple Java code used for this call is this.
Calc.java
public class Calc {
public static int addTwoNumbers(int a, int b) {
return a + b;
}
}
As a static method of the Calc class, it does a simple job of ** taking two integer type arguments and returning the sum of them as a return value **. Let's call this addTwoNumbers method from Common Lisp. The code looks like this (↓)
calc.lisp
(defun add-two-num (num1 num2)
(let ((result (jstatic "addTwoNumbers" "Calc" num1 num2)))
result))
jstatic is a built-in function for calling Java static methods. The first argument is ** the method name (character string) ** to be called, the second argument is ** the class to which the target method belongs **, and the third and subsequent arguments are ** method arguments **. When evaluated, the method execution result is returned, it is bound to result, and it is output at the end.
Even if you look at the sample program, only this is written, but of course the Java code cannot be called as it is, so compile it and pass it through the classpath. I don't know much about that, so if I write it for people,
$ javac Calc.java
$ ls
Calc.class Calc.java calc.lisp
If it becomes, it is a success. If the compilation does not pass, something is wrong and please check again. While saying that, there may be no mistakes with this length of code.
Now let's move on to the REPL and actually call it!
CL-USER> (load "/Path/to/calc.lisp")
T
CL-USER> (add-to-classpath "/Path/to/Calc.java/")
("/Path/to/java-class")
CL-USER> (add-two-num 2 3)
5
CL-UER> (add-two-num -1 9)
8
You can call it correctly! add-to-classpath is an ABCL built-in function for passing paths to Java class files. In Java, you can pass the classpath, but this is more dynamic and easier to understand (personal view).
I'm not so happy to be able to call only the Static method, so let's check the Dynamic dispatch of the method. The code used for this trial is as follows.
Hero.java
public class Hero {
private int hp;
private int mp;
public Hero() {
hp = 100;
mp = 100;
}
public void getDamage(int damage){
hp -= damage;
}
public void useMagic(int consumption){
mp -= consumption;
}
public String showStatus(){
return "HP: "+hp+", MP: "+mp;
}
public static void main(String[] argv){
Hero hero = new Hero();
}
}
There is a hero who has "HP: 100" and "MP: 100" when he is born. It takes a certain amount of damage when attacked, and consumes MP when using magic. When you check the status, the remaining HP and MP will be displayed.
Please leave this kind of class design aside, and think that you have defined a class like this.
hero.lisp
(add-to-classpath "/Path/to/java-class-file")
(defun hero-status ()
(let* ((hero-class (jclass "Hero"))
(hero-instance (jnew (jconstructor "Hero")))
(method (jmethod hero-class "showStatus"))
(result (jcall method hero-instance)))
result))
First, instantiate this Hero class and call the showStatus method. The initial state "HP: 100, MP: 100" should be output.
Earlier we evaluated add-to-classpath at the top level of the REPL, but of course if you put it in a Lisp file, it will be executed at load time.
jclass takes a string as an argument and returns a Java class reference with the name of that string. What is returned here is a class object, not an instance.
jnew takes a constructor object as an argument and creates an instance. Please note that it cannot be instantiated unless the constructor object is acquired by (jconstructor "class name (character string)") [^ Note 1].
jmethod gets the method to call as an object. Pass the Java class object and method name, and if necessary, the argument type class [^ Note 2].
Finally, pass the method object acquired earlier and the instance object to jcall, and execute the method.
When you actually execute it, it should look like the following.
CL-USER> (load "/Path/to/hero.lisp")
T
CL-USER> (hero-status)
"HP: 100, MP: 100"
Next, after using the HP decrement method and MP decrement method, execute the status confirmation method. Both methods will subtract the value by passing the value to be reduced as an argument.
The Lisp expression for calling looks like this.
hero-extend.lisp
(defun extended-hero-status (hp mp)
(let* ((hero-class (jclass "Hero"))
(hero-instance (jnew (jconstructor "Hero")))
(java-int-class (jclass "int"))
(get-damage (jmethod hero-class "getDamage" java-int-class))
(use-magic (jmethod hero-class "useMagic" java-int-class))
(method (jmethod hero-class "showStatus")))
(jcall get-damage hero-instance hp)
(jcall use-magic hero-instance mp)
(let (result (jcall method hero-instance))
result)))
It will be written in a Java-like way, but when you actually evaluate this function,
CL-USER> (load "/Path/to/exted-hero.lisp")
T
CL-USER> (extended-hero-status 5 10)
"HP: 95, MP: 90"
I think that you can see that it is working properly.
What did you think. Did you get the feel? With ABCL, you may be able to use Java libraries relatively easily with ComonLisp and incorporate various programs written in the Java language into your CL project.
I didn't introduce it this time, but of course it is possible to call Common Lisp from the Java language. See the user reference mentioned above for details.
We look forward to your suggestions and impressions of mistakes.
[^ Note 1]: I was addicted to this because I didn't understand the argument of jnew. [^ Note 2]: Instead of passing the argument itself here, pass the typeclass object of the argument. Specifically, the method to be dispatched is determined by passing something like (jclass "int").
Recommended Posts