JDK

Java 16 新機能(1)

3月16日にJava 16がリリースされました。早速,新しい機能を試してみることにします。

リリースノート(Oracle)

まずは,Foreign Linker APIというものです。これはJavaから他の言語で作成されたネイティブAPIを呼び出す機能です。従来はJNIという仕組みで行っていました。しかし,JNIはC言語のヘッダファイルを用意して呼出規約を定義する必要があり,非常に面倒でした。 Foreign Linker API はまだincubaterという位置づけですが,非常に簡単にネイティブ関数を呼び出すことができる魅力的な機能です。早く正式なAPIとしてJDKに入れて欲しいと願っています。

以下のプログラムではC言語の文字列長を返すstrlen関数を呼び出しています。strlenに”Hello World”という文字列を渡して,文字列の長さが11と返されています。

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import jdk.incubator.foreign.Addressable;
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.LibraryLookup;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
public class ForeignLinker
{
	public static void main(String[] args) throws Throwable
	{
		int length = callStrlen("Hello World");
		System.out.println(length);
	}
	public static int callStrlen(String str) throws Throwable
	{
		int result = 0;
		Addressable address = LibraryLookup.ofDefault().lookup("strlen").get();
		MethodType methodType = MethodType.methodType(int.class, MemoryAddress.class);
		FunctionDescriptor descriptor = FunctionDescriptor.of(CLinker.C_LONG, CLinker.C_POINTER);
		CLinker clinker = CLinker.getInstance();
		MethodHandle strlen = clinker.downcallHandle(address, methodType, descriptor);
		try (MemorySegment cString = CLinker.toCString(str))
		{
			result = (int)strlen.invokeExact(cString.address());
		}
		return result;
	}
}

警告:実験的なモジュールを使用しています: jdk.incubator.foreign
警告1個
WARNING: Using incubator modules: jdk.incubator.foreign
11

ちなみにこれをJNIで実現しようとすると,以下のようになります。

[Strlen.java]
public class Strlen
{
    static
    {
        System.loadLibrary("Strlen");
    }
    public static void main(final String[] args)
    {
        Strlen instance = new Strlen();
        int result = instance.execStrlen("Hello World");
        System.out.println(result);
    }
    public native int execStrlen(String arg);
}
[Strlen.h]
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Strlen */

#ifndef _Included_Strlen
#define _Included_Strlen
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Strlen
 * Method:    execStrlen
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_Strlen_execStrlen
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
[Strlen.c]
#include <string.h>
#include "Strlen.h"

JNIEXPORT jint JNICALL Java_Strlen_execStrlen(JNIEnv *env, jobject obj, jstring arg)
{
    const char* bytes = (*env) -> GetStringUTFChars(env, arg, NULL);
    size_t length = strlen(bytes);
    (*env) -> ReleaseStringUTFChars(env, arg, bytes);
    return length;
}