LoginSignup
103
60

More than 1 year has passed since last update.

Java 18新機能まとめ

Last updated at Posted at 2022-03-22

Java 18が2022/3/22にリリースされました。

https://mail.openjdk.org/pipermail/jdk-dev/2022-March/006458.html
機能的に多くのプログラマに関係ありそうな変更はUTF-8がデフォルトになったくらい。あとは簡単なWebサーバーが入ったことかな。

詳細はこちら
JDK 18 Release Notes
Java SE 18 Platform JSR 393
OpenJDK JDK 18 GA Release

APIドキュメントはこちら
Overview (Java SE 18 & JDK 18)

変更点まとめはこちら
https://docs.oracle.com/en/java/javase/18/docs/api/new-list.html

APIの差分はこちら。
https://cr.openjdk.java.net/~iris/se/18/latestSpec/apidiffs/overview-summary.html

MacやLinuxでのインストールにはSDKMAN!をお勧めします

Oracle OpenJDK以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。(まだ用意されていないものもあります。3/23時点ではOracle JDK, Corretto 18だけかな)

アップデートは4月に18.0.1が、7月に18.0.2がリリースされることになります。

JEP

大きめの変更はJEPでまとまっています。
https://openjdk.java.net/projects/jdk/18/

今回は9個のJEPが取り込まれました。すでにプレビューなどで出ていたものが3つあり、新たに取り込まれたものは6です。非推奨が1つあるので、新機能としては5つですね。

JEP 400: UTF-8 by Default
JEP 408: Simple Web Server
JEP 413: Code Snippets in Java API Documentation
JEP 416: Reimplement Core Reflection with Method Handles
JEP 417: Vector API (Third Incubator)
JEP 418: Internet-Address Resolution SPI
JEP 419: Foreign Function & Memory API (Second Incubator)
JEP 420: Pattern Matching for switch (Second Preview)
JEP 421: Deprecate Finalization for Removal

ツール

ツール関係の変更としては次の3つのJEPがあります。
JEP 400: UTF-8 by Default
JEP 408: Simple Web Server
JEP 413: Code Snippets in Java API Documentation

JEP 400: UTF-8 by Default

APIのデフォルトエンコーディングがUTF-8になりました。これでネットでダウンロードしたファイルを表示するプログラムがWindowsだけ文字化けするみたいなことが起きにくくなります。

環境変数では、file.encodingがUTF-8になります。

C:\Users\naoki>java\jdk\jdk-18\bin\java -XshowSettings:properties -version
Property settings:
    file.encoding = UTF-8
...
    line.separator = \r \n
    native.encoding = MS932
...
    sun.jnu.encoding = MS932
...
    sun.stderr.encoding = ms932
    sun.stdout.encoding = ms932
...

openjdk version "18" 2022-03-22
OpenJDK Runtime Environment (build 18+36-2087)
OpenJDK 64-Bit Server VM (build 18+36-2087, mixed mode, sharing)

Java 17ではOSごとに違いWindowsではMS932でした。

C:\Users\naoki>java\jdk\jdk-17\bin\java -XshowSettings:properties -version
Property settings:
    file.encoding = MS932
...
    line.separator = \r \n
    native.encoding = MS932
...
    sun.jnu.encoding = MS932
...
    sun.stderr.encoding = ms932
    sun.stdout.encoding = ms932
...

openjdk version "17" 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)

ただ、いままで通りOSごとに違う設定にしたい場合というのもあると思います。その場合は、環境変数file.encodingCOMPATを指定します。そうすると、実際のfile.encodingの値が環境ごとのエンコーディング設定になります。

C:\Users\naoki>java\jdk\jdk-18\bin\java -Dfile.encoding=COMPAT -XshowSettings:properties -version
Property settings:
    file.encoding = MS932

native.encodingがJava 17で導入されているので、COMPATを選ぶときはfile.encodingにその値が設定されます。

C:\Users\naoki>javac C:\Users\naoki\IdeaProjects\untitled3\src\main\java\projava\WebServer.java
C:\Users\naoki\IdeaProjects\untitled3\src\main\java\projava\WebServer.java:29: エラー: この文字(0x83)は、エンコーディン
グwindows-31jにマップできません
                        <body><h1>Hello</h1>It works!<br>縺ッ繧阪?シ</body></html>
                                                             ^
エラー1個
C:\Users\naoki>java\jdk\jdk-18\bin\javac C:\Users\naoki\IdeaProjects\untitled3\src\main\java\projava\WebServer.java

Charset.forName("default")はJava 17ではUS-ASCIIを返していました。

C:\Users\naoki>java\jdk\jdk-17\bin\jshell
|  JShellへようこそ -- バージョン17
|  概要については、次を入力してください: /help intro

jshell> java.nio.charset.Charset.forName("default")
$1 ==> US-ASCII

これがJava 18ではUnsupportedCharsetExceptionを投げるようになります。

C:\Users\naoki>java\jdk\jdk-18\bin\jshell
|  JShellへようこそ -- バージョン18
|  概要については、次を入力してください: /help intro

jshell> java.nio.charset.Charset.forName("default")
|  例外java.nio.charset.UnsupportedCharsetException: default
|        at Charset.forName (Charset.java:527)
|        at (#1:1)

JEP 408: Simple Web Server

ファイルを扱う簡単なWebサーバーが用意されました。
jwebserverコマンドで、カレントディレクトリを扱うWebサーバーが起動します。

C:\Users\naoki>echo ^<h1^>hello^</h1^>It^ works > index.html

C:\Users\naoki>java\jdk\jdk-18\bin\jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving C:\Users\naoki and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/
127.0.0.1 - - [05/3譛・2022:22:14:03 +0900] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [05/3譛・2022:22:14:03 +0900] "GET /favicon.ico HTTP/1.1" 404 -

8000番ポートが使われます。
image.png

起動オプションには次のようなものがあります。

オプション 用途
-h / -? / --help ヘルプメッセージの表示
-b addr / --bind-addess addr アドレスをバインドする。デフォルトはループバック
-d dir / --directory dir 提供するディレクトリ。デフォルトはカレントディレクトリ
-o level or --output level 出力フォーマット。 none / info / verbose デフォルトはinfo
-p port or --port port ポート番号。デフォルトは8000
-version or --version バージョンの出力

API

APIから起動することもできます。SimpleFileServer

jshell> import com.sun.net.httpserver.SimpleFileServer

jshell> import com.sun.net.httpserver.SimpleFileServer.OutputLevel

jshell> var server = SimpleFileServer.createFileServer(new InetSocketAddress(8000), Path.of("c:/Users/naoki"), OutputLev
el.VERBOSE)
server ==> sun.net.httpserver.HttpServerImpl@2f7c7260

jshell> server.start()

jshell> 127.0.0.1 - - [05/3譛・2022:23:08:05 +0900] "GET / HTTP/1.1" 200 -
Resource requested: c:\Users\naoki
> Cookie: _redmine_session=YWNTYi9CVTNqclBQcG9WOFZ4d3o5bHJZTDNIWlUwcnJhZml0c05ac0tCQzZQVVlYelJPY01PaGxrMjdNbDhvazkwL1lEQnhTNSs3SDBSVm5HT3hERGQ4MVc2K2hRV1ptNE4yTjMzbFlKWkpQZmI0aWhVWFJLNkJhMHFtOS8zVURnbUJVZGJyOTdIL0lVd1g5YlJ0OEVTVW90WGs5MTdMRDRkOU1SOUxqYnJ2clJ4bTQzMkt1am4zSlFSa0pFWC9KcUZoSnJqelcrMTJzQ3djUEs4ZXp0dGJSRnBNVTFSYXRwUnFFUGVTMlBEMDRvZGdOZVc2ejVuYXZjY09Fdnp4WjNkUTIydStTMFh4RUtpRnAway9vOHJKbkNzNFFIbTZJc0dOY3ZlWUFTbENRSlRKbjBROU55ZVJDaGtoSW9WNnV2VnpLc1BIYkZySmg3Z2ZwajM1dkM0V3JNemN6b1NaQTBMN0NHRjdUYjcyajlYcm5lOW9RbjN5eVNyUnVOYVRQdUJobjNmY2hZUFlQT3E1WkdROW5vWGc1eDVPZnVhOERwM09PazhibVp0TGYreTVId2tuMDdxUmlKNU5DRFVXOVlMWHV3S08ydCtpaExxdElPdEZDc0hhMzhSUTRKTDBjdDZETjhPT0R2eCtoOGFkR2NqcWtXMHlRVUFPZ1BuQ2szZGVKM1Z3MHp6Ky9KS0NmUkdNNVMxbVdDQUxxbkUybk96L2Y4ZytteGdIQ3BhaXBPeWlWNnBKc2ltMUZJalpsSy93K3R1SjUzRGRCQUJwWnkweSs3UT09LS1IUE1VSmN1dC9ZR1hJTytSSFlrQW5RPT0%3D--672fdb2f2f192abdf0e3b3d62c6eb9001a1110eb
> Accept-encoding: gzip, deflate
> Sec-fetch-dest: document
> Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
> Sec-fetch-user: ?1
> Connection: keep-alive
> Sec-fetch-site: none
> Host: localhost:8000
> Sec-fetch-mode: navigate
> User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0
> Accept-language: ja,en-US;q=0.7,en;q=0.3
> Upgrade-insecure-requests: 1
>
< Date: Sat, 05 Mar 2022 14:08:05 GMT
< Last-modified: Sat, 5 Mar 2022 13:44:43 GMT
< Content-type: text/html
< Content-length: 25
<

createFileServerに与えるPathは絶対パスで指定します。

JEP 413: Code Snippets in Java API Documentation

Javadocにコードスニペットを含むことができます。使う機会があるかどうかは別として、わりと面白い。

基本は{@snippet :から}で囲むものです。

/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) {
 *     System.out.println("v: " + v.get());
 * }
 * }
 */

制約として、/**/のコメントは使えません。また中カッコ{}は対応している必要があります。

スニペットとして外部ファイルを指定することもできます。

/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet file="ShowOptional.java" region="example"}
 */

// @startから// @endまでがスニペットとして使われる感じですね。

public class ShowOptional {
    void show(Optional<String> v) {
        // @start region="example"
        if (v.isPresent()) {
            System.out.println("v: " + v.get());
        }
        // @end
    }
}

@highlightで強調を指定することもできます。\bは単語境界を示すので、\barg\bの場合argsは強調されずargだけが強調されます。

/**
 * {@snippet :
 *   public static void main(String... args) {
 *       for (var arg : args) {                 // @highlight region regex = "\barg\b"
 *           if (!arg.isBlank()) {
 *               System.out.println(arg);
 *           }
 *       }                                      // @end
 *   }
 *   }
 */

@replaceで、コンパイルが通らない文字列に置き換えることもできますね。

/**
 * A simple program.
 * {@snippet :
 * class HelloWorld {
 *     public static void main(String... args) {
 *         System.out.println("Hello World!");  // @replace regex='".*"' replacement="..."
 *     }
 * }
 * }
 */

@linkでJavadocへのリンクを設定することもできます。

/**
 * A simple program.
 * {@snippet :
 * class HelloWorld {
 *     public static void main(String... args) {
 *         System.out.println("Hello World!");  // @link substring="System.out" target="System#out"
 *     }
 * }
 * }
 */

言語機能

言語機能の変更としては、Pattern Matching for switchが2ndプレビューになっています。

JEP 420: Pattern Matching for switch (Second Preview)

パターンマッチがswitchで使えるようになります。
2ndプレビューになりました。今回大きな変更がなければJava 19で正式機能になりそうです。
Java 18では、使うときに--enable-previewが必要です。

「といってもパターンマッチってそんなに出番ないのでは?」
と思うかもしれませんが、大きな影響が。

switchcase nullが書けるようになります。

jshell> String s = null
s ==> null

jshell> switch (s) {
   ...>     case "test" -> "テスト";
   ...>     case null -> "ぬるぽ";
   ...>     default -> "hello";
   ...> }
$13 ==> "ぬるぽ"

case nullがない場合は従来どおりNullPointerExceptionです。

jshell> switch (s) {
   ...>     case "test" -> "テスト";
   ...>     default -> "hello";
   ...> }
|  例外java.lang.NullPointerException: Cannot invoke "String.hashCode()" because "<local0>" is null
|        at (#12:1)

""nullで同じ処理を行う場合などに併記できるのが便利です。

jshell> switch(s){
   ...>     case null, "" -> "empty";
   ...>     default -> s;
   ...> }
$17 ==> "empty"

defaultnullで同じ処理したいときにアロースタイルで書けないんでは?という疑問に応えて、defaultcaseに書けるようになっています。

jshell> switch (s) {
   ...>     case "one" -> 1;
   ...>     case "two" -> 2;
   ...>     case null, default -> -1;
   ...> }
$21 ==> -1

Java 17の1st previewの時点ではswitchにnullリテラルを渡すとjavacが落ちていました。

>jshell --enable-preview
|  JShellへようこそ -- バージョン17
|  概要については、次を入力してください: /help intro

jshell> switch(null) {
   ...>     default -> "hello";
   ...> }
コンパイラで例外が発生しました(17)。Bug Database (http://bugs.java.com)で重複が...

Java 18で修正されて正しくぬるぽが出るようになっています。

jshell> switch (null) {
   ...>     default -> "hello";
   ...> }
|  例外java.lang.NullPointerException
|        at Objects.requireNonNull (Objects.java:208)
|        at (#33:1)

で、パターンマッチですが、型と変数をswtichに書けるようになりました。これをタイプパターンといいます。

jshell> Object o = "abc"
o ==> "abc"

jshell> switch (o) {
   ...>     case String s -> "--%s--".formatted(s);
   ...>     case Integer i -> "%03d".formatted(i);
   ...>     default -> o.toString();
   ...> }
$35 ==> "--abc--"

タイプパターンのあとに&&でつなげて条件を書くことでガード節とすることができます。

jshell> o = "helo"
o ==> "helo"

jshell> switch (o) {
   ...>     case String s && s.length() < 5 -> "--%s--".formatted(s);
   ...>     case String s -> s.toUpperCase();
   ...>     case Integer i -> "%05d".formatted(i);
   ...>     default -> o.toString();
   ...> }
$42 ==> "--helo--"

ただし、3rdプレビューでは&&ではなくwhenを使うようです。

switch (o) {
    case String s when s.length() < 5 -> "--%s--".formatted(s);
    case String s -> s.toUpperCase();
    case Integer i -> "%05d".formatted(i);
    default -> o.toString();
}

3rdプレビューが来たということは、Java 19での正式化はないということでもあります。
http://openjdk.java.net/jeps/8282272

定数パターンとタイプパターンをひとつのswitchで使うこともできます。定数パターンに使えるのはこれまでどおり整数、文字列、enumです。

jshell> s = "helo"
s ==> "helo"

jshell> switch (s) {
   ...>     case "test" -> "テスト";
   ...>     case String s && s.length() < 5 -> s.toUpperCase();
   ...>     default -> "too long";
   ...> }
$29 ==> "HELO"

case句のタイプパターンで定義した変数のスコープは、case句の中だけです。
ここではswitchの外にあるのと同じく変数sを定義してcase句で上書きしていますが、switchの外に影響はありません。

jshell> s = "helo"
s ==> "helo"

jshell> switch (s) {
   ...>     case String s && s.length() < 5 -> (s="Hello").toUpperCase();
   ...>     default -> "too long";
   ...> }
$30 ==> "HELLO"

jshell> s
s ==> "helo"

Java 19ではレコードに対するパターンがプレビューとして導入される予定です。
https://openjdk.java.net/jeps/405

return switch(n) {
    case IntExpr(int i) -> i;
    case NegExpr(Expr n) -> -eval(n);
    case AddExpr(Expr left, Expr right) -> eval(left) + eval(right);
    case MulExpr(Expr left, Expr right) -> eval(left) * eval(right);
    default -> throw new IllegalArgumentException(n);
};

API

[JEP 416: Reimplement Core Reflection with Method Handles]
JEP 421: Deprecate Finalization for Removal
JEP 418: Internet-Address Resolution SPI
JEP 417: Vector API (Third Incubator)
[JEP 419: Foreign Function & Memory API (Second Incubator)]

小さいもの

java.lang.Math / java.lang.StrictMath

いろいろ追加されています。

ceilDiv
ceilMod
divideExact
floorDivExact
unsignedMultiplyHigh

Duration.isPositive

Duration.isZeroDuration.isNegativeはこれまであったのにisPositiveがなかったので追加されました。

HttpRequest.Builder.HEAD()

メソッドをHEADとしてビルダーを作ります。

JEP 416: Reimplement Core Reflection with Method Handles

java.lang.reflect.Methodjava.lang.reflect.Constructorjava.lang.reflect.FieldをMethod Handlesを使って実装しなおします。
APIへの変更はなく、実装の変更です。

JEP 421: Deprecate Finalization for Removal

OSリソースの開放漏れを防ぐために導入されたファイナライザですが、オブジェクトがGC対象になってからfinalizeがいつ呼び出されるかわからなかったり、GC対象になったはずのオブジェクトを再び参照させたり、スレッドや呼び出し順をコントロールできなかったり、いろいろな問題がありました。
セキュリティ脆弱性の原因になったり、パフォーマンス悪化につながったりということで、非推奨、そのうち削除ということになりました。

--finalization=disabledをつけて実行することで、ファイナライザが無効になって、GC時にも実行されなくなります。デフォルトではファイナライザは有効ですが、今後のバージョンでデフォルト無効になり、さらにその先のバージョンでファイナライザは取り除かれます。
Object.finalize()Graphics.finalize()のようなfinalizeメソッドは@Deprecatedになります。

JEP 418: Internet-Address Resolution SPI

InetAddress.getByNameのようなAPIはシステムに組み込まれた名前解決を使っていましたが、SPIとして提供できるようになりました。

JEP 417: Vector API (Third Incubator)

AVX命令のような、複数のデータに対する計算を同時に行う命令をJavaから利用できるようになります。
Project Panamaのひとつとして開発されていました。
Java 16でインキュベータとして導入されましたが、今回3rdインキュベータになりました。今回、ArmのSVE命令をサポートしています。

使うためには実行時やコンパイル時に--add-modules jdk.incubator.vectorをつける必要があります。

import jdk.incubator.vector.*;
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;

void vectorComputation(float[] a, float[] b, float[] c) {

    for (int i = 0; i < a.length; i += SPECIES.length()) { // SPECIES.length() = 256bit / 32bit -> 8
        VectorMask<Float> m = SPECIES.indexInRange(i, a.length); // 端数がマスクされる
                                                                 // a.lengthが11でiが8のとき最初の3つしか要素がないので [TTT.....]
		// FloatVector va, vb, vc;
        FloatVector va = FloatVector.fromArray(SPECIES, a, i, m);
        FloatVector vb = FloatVector.fromArray(SPECIES, b, i, m);
        FloatVector vc = va.mul(va).
                    add(vb.mul(vb)).
                    neg();
        vc.intoArray(c, i, m);
    }
}

利用できるのは次の6つの型です。それぞれに対応するVector型があって、これが基本になります。

bit幅 Vector
byte 8 ByteVector
short 16 ShortVector
int 32 IntVector
long 64 LongVector
float 32 FloatVector
double 64 DoubleVector

ただ、利用するにはVectorSpeciesが必要です。利用したいVectorにSPECIES_*という定数が用意されているので、それを使います。*は一度に計算するbit数ですね。

jshell> FloatVector.SP
SPECIES_128         SPECIES_256         SPECIES_512         SPECIES_64          SPECIES_MAX         SPECIES_PREFERRED   

MAXではそのハードウェアで使える最大、PREFERREDは推奨ビット数だけど、同じになるんじゃないのかな。ここでは256bitが推奨されて、floatが8個同時に計算できるようになっていますね。

jshell> FloatVector.SPECIES_PREFERRED
$11 ==> Species[float, 8, S_256_BIT]

ハードウェアで使えるbit数は搭載CPUに依存しますが、普通のIntel/AMDであれば256、XEONとか つよつよCPUなら512かな。M1は128でした。ハードウェアでサポートされないbit数を使おうとするとソフトウェア処理になるので遅くなります。

実際のVectorはfrom*というメソッドで取得します。fromArray、fromByteArray、fromByteBufferが用意されています。インキュベータに入る前はfromValuesがあったのですが、なくなってますね。

Vectorを得られたら、用意されたメソッドで計算します。ひととおりの算術命令はあります。

jshell> va.
abs()                   add(                    addIndex(               bitSize()               blend(                  
broadcast(              byteSize()              compare(                div(                    elementSize()           
elementType()           eq(                     equals(                 fma(                    getClass()              
hashCode()              intoArray(              intoByteArray(          intoByteBuffer(         lane(                   
lanewise(               length()                lt(                     max(                    min(                    
mul(                    neg()                   notify()                notifyAll()             pow(                    
rearrange(              reduceLanes(            reduceLanesToLong(      reinterpretAsBytes()    reinterpretShape(       
selectFrom(             shape()                 slice(                  sqrt()                  sub(                    
test(                   toArray()               toDoubleArray()         toIntArray()            toLongArray()           
toShuffle()             toString()              unslice(                viewAsFloatingLanes()   viewAsIntegralLanes()   
wait(                   withLane(      

ところで、こういったメソッド呼び出しの内部でAVX命令などを呼び出すのでは遅くなるんではという気がしますが、実際にはJVM intrinsicsという仕組みでJITコンパイラがこれらのメソッド呼び出しをネイティブ関数呼び出しに置き換えます。

JEP 419: Foreign Function & Memory API (Second Incubator)

2ndインキュベータですが、Foreign Function APIの前身であるForeign Linker APIはJava 16から、Foreign Memory APIはJava 14からインキュベータで、結構ながくインキュベータやってます。

Foreign Memory API

ヒープ外のメモリをアクセスする方法としては、ByteBufferを使う方法やUnsafeを使う方法、JNIを使う方法がありますが、それぞれ一長一短があります。
ByteBufferでdirect bufferを使う場合、intで扱える範囲の2GBまでに制限されたり、メモリの解放がGCに依存したりします。
Unsafeの場合は、性能もいいのですが、名前が示すとおり安全ではなく、解放済みのメモリにアクセスすればJVMがクラッシュします。
JNIを使うとCコードを書く必要があり、性能もよくないです。

ということで、ヒープ外のメモリを直接扱うAPIがJava 14でインキュベータモジュールとして導入され、今回5バージョン目のインキュベータです。
次のようなコードになります。

import jdk.incubator.foreign.*;
import java.nio.ByteOrder;

VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   for (int i = 0 ; i < 25 ; i++) {
        intHandle.set(seg, i * 4, i);
   }
}

コンパイル、実行では--add-modules jdk.incubator.foreignを付けてモジュールを読み込む必要があります。
パッケージもモジュール名と同様、jdk.incubator.foreignになっています。

MemoryHandlesからwithOffsetとwithStrideが消えましたね。

jshell> import jdk.incubator.foreign.*

jshell> MemoryHandles.
asAddressVarHandle(   asUnsigned(           class                 collectCoordinates(   dropCoordinates(
filterCoordinates(    filterValue(          insertCoordinates(    permuteCoordinates(   varHandle(

Java 15ではMemoryHandlesからVarHandleを得るメソッドが拡充してました。

jshell> MemoryHandles.
asAddressVarHandle(   asUnsigned(           class                 collectCoordinates(   dropCoordinates(
filterCoordinates(    filterValue(          insertCoordinates(    permuteCoordinates(   varHandle(
withOffset(           withStride(

Java 14では次の3つのメソッドしかありませんでした。

jshell> MemoryHandles.
class         varHandle(    withOffset(   withStride(

Java 16ではMemoryAccessというユーティリティが用意されて、VarHandleを使わずにシンプルにかけるようになっています。

import jdk.incubator.foreign.*;

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   for (int i = 0 ; i < 25 ; i++) {
        MemoryAccess.setIntAtOffset(i * 4, i);
   }
}

Foreign Function API

ネイティブライブラリの呼び出しを行う。
外部メモリのアクセスにはForeign Memory Access APIを使う
JNIの代替

Java 16でForeign Linkerとして1stインキュベータになり、今回3バージョン目のインキュベータです。

たとえばこんな感じのC関数があって。

size_t strlen(const char *s);

こんな感じでMethodHandleを取り出して。

CLinker linker = CLinker.systemCLinker();
MethodHandle strlen = linker.downcallHandle(
    linker.lookup("strlen").get(),
    FunctionDescriptor.of(JAVA_LONG, ADDRESS)
);

こんな感じで呼び出すようです。

MemorySegment str = implicitAllocator().allocateUtf8String("Hello");
long len          = strlen.invoke(cString);  // 5
103
60
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
103
60