I tried hitting a Java method from ABCL

Introduction

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.

Reference documents

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.

-Slyrus's Github page

If you get lost, it may be a good idea to hit the source code.

-ABCL Home Page

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.

Execution environment

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. .. ..

Java static method call

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).

Java dynamic method call

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.

in conclusion

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

I tried hitting a Java method from ABCL
[Java] I tried to make a maze by the digging method ♪
java I tried to break a simple block
I tried to break a block with java (1)
I tried running Java on a Mac terminal
[JDBC] I tried to make SQLite3 database access from Java into a method for each SQL statement.
Call a method with a Kotlin callback block from Java
I tried to create a Clova skill in Java
I tried to make a login function in Java
I tried using Log4j2 on a Java EE server
I tried calling Java / Objective-C native code from Flutter
I tried OCR processing a PDF file with Java
I made a Wrapper that calls KNP from Java
I tried scraping a stock chart using Java (Jsoup)
Create a java method [Memo] [java11]
I tried Drools (Java, InputStream)
I tried using Java REPL
I tried metaprogramming in Java
I tried to create a java8 development environment with Chocolatey
I tried to modernize a Java EE application with OpenShift.
[JDBC] I tried to access the SQLite3 database from Java.
I tried to convert a string to a LocalDate type in Java
I tried to make a client of RESAS-API in Java
I tried OCR processing a PDF file with Java part2
I created a PDF in Java.
I made a shopify app @java
I tried a little digdag docker.run_options
A person writing C ++ tried writing Java
Run a batch file from Java
I tried to interact with Java
I tried UDP communication with Java
Access Teradata from a Java application
I tried to explain the method
I tried the Java framework "Quarkus"
I tried using Java8 Stream API
I tried using JWT in Java
I tried to summarize Java learning (1)
I tried to summarize Java 8 now
I tried using Java memo LocalDate
I tried using GoogleHttpClient of Java
[Note] What I learned in half a year from inexperienced (Java)
[Note] What I learned in half a year from inexperienced (Java) (1)
I made a method to ask for Premium Friday (Java 8 version)
[Note] What I learned in half a year from inexperienced (Java) (3)
I tried to generate a C language program source from cURL
I tried using Elasticsearch API in Java
I tried a calendar problem in Ruby
Java method
java (method)
I tried using UICollectionViewListCell added from Xcode12.
I tried learning Java with a series that beginners can understand clearly
Call Java method from JavaScript executed in Java
I tried Cassandra's Object Mapper for Java
I tried to summarize Java lambda expressions
Java9 was included, so I tried jshell.
I tried the new era in Java
Try running a Kubernetes Job from Java
I tried playing with BottomNavigationView a little ①
I tried using OpenCV with Java + Tomcat
Java method
[Java] method