Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JJUG CCC 2026 Spring: JSpecify で実現する Kotlin フレン...

JJUG CCC 2026 Spring: JSpecify で実現する Kotlin フレンドリーな Java API 設計

Avatar for ternbusty

ternbusty

May 29, 2026

More Decks by ternbusty

Other Decks in Programming

Transcript

  1. © LY Corporation 2 Ayako Hayasaka LINEヤフー株式会社 Software Engineer 2023

    年度⼊社 SWAT チーム所属 Web バックエンド領域で全社横断的な技術⽀援を実施 Java ⾔語サポートチーム所属 メインは Spring Boot + Java / Kotlin 外部登壇 • JJUG CCC 2025 Fall: Virtual Thread Deep Dive • CloudNative Days Winter 2025: ⼀週間で作る 低レイヤコンテナランタイム • Open Source Summit North America 2026: Building a Shared, Persistent Virtual Filesystem for WebAssembly OSS Contribute • Google Summer of Code 2026: Tail call support in the Kotlin/Wasm backend (Kotlin Foundation, 進⾏中) © LY Corporation 2
  2. © LY Corporation 3 LINEヤフーでの Server Side Kotlin の広がり https://techblog.lycorp.co.jp/ja/20260318b

    サーバーサイド開発で使⽤している開発⾔語はどれですか? Java Kotlin
  3. © LY Corporation LINEヤフーでの Server Side Kotlin の広がり • LINEメッセージングプラットフォーム,

    LINE公式アカウント, LINE Store, LINEギフト, LINEポイント, LINEミニアプリ, Yahoo!フリマ, Yahoo!カレンダー, Yahoo!ファイナンス など多くの採⽤事例・求⼈あり • 社内の Java サポートチームでも Server Side Kotlin のサポートを開始 • Java チームで提供しているライブラリを Kotlin から使う場合の サンプルレポジトリ提供 • Java エンジニアのための Kotlin ガイド等ドキュメント整備 • Java チームで提供しているライブラリを Kotlin から利⽤しやすく する対応 4
  4. © LY Corporation LINEヤフーでの Server Side Kotlin の広がり • LINEメッセージングプラットフォーム,

    LINE公式アカウント, LINE Store, LINEギフト, LINEポイント, LINEミニアプリ, Yahoo!フリマ, Yahoo!カレンダー, Yahoo!ファイナンス など多くの採⽤事例・求⼈あり • 社内の Java サポートチームでも Server Side Kotlin のサポートを開始 • Java チームで提供しているライブラリを Kotlin から使う場合の サンプルレポジトリ提供 • Java エンジニアのための Kotlin ガイド等ドキュメント整備 • Java チームで提供しているライブラリを Kotlin から利⽤しやすく する対応 5
  5. © LY Corporation Java と null アノテーションの⻑く泥臭い歴史 Kotlin から Java

    のライブラリを利⽤する際に困ること Java ライブラリ側で必要な対応 Platform Type の検出⽅法 まとめ 01 03 02 04 05 6 Agenda
  6. © LY Corporation 前提知識: Java における null の課題 • Java

    では、すべての参照型に null が代⼊可能 • 型レベルで、nullable か否かを表現することができない • 結果として、静的解析では null の有無を判定できず、実⾏時に初めて NullPointerException (NPE) が発⽣する → どうにかして、静的解析時に参照できるような、Nullability を表現する⽅法が 欲しい! 7 Annotation を付与することで Nullability を 表現する仕組みがあればいいのでは?
  7. © LY Corporation 歴史: null 安全アノテーションの乱⽴ • JetBrains, Spring Framework

    などがそれぞれ独⾃の @Nullable or @NotNull or @NonNull アノテーションを定義 • 同じ名前だが別物、IDE で警告が出ない等の問題が顕在化 • 統⼀されたアノテーションを作ろうとした JSR-305 (javax.annotations.*) も頓挫 nullの10億ドルの過ちから未来へ Spring Boot 4とJSpecifyが描くnull安全設計 8
  8. © LY Corporation JSpecify の登場 • 2021 年に Google が⽴ち上げ、2024

    年に 1.0.0 をリリース • Broadcom (Spring プロジェクト)、JetBrains、Oracle (OpenJDK)、 Uber (NullAway) など主要なステークホルダーに⽀えられている *1 • Spring Framework 7 および Spring Boot 4 以降で採⽤ *2 • Spring Boot 4 以降では Spring の主要な API は JSpecify アノテーション付き! • コレクションの中⾝の型等にも Annotation を付与できる *1: https://jspecify.dev/about/ *2: https://spring.io/blog/2025/03/10/null-safety-in-spring-apps-with-jspecify-and-null-away List<@Nullable String> role; 9
  9. © LY Corporation 10 ⼀⽅そのころ Kotlin は……? Kotlin から Java

    のライブラリを利⽤する際に困ること 02
  10. © LY Corporation Kotlin の null 安全性 • Java と違い、null

    を許容する型 (例: String?) とそうでない型 (例: String) を 型システムレベルで明⽰しているので、静的解析でエラーにできる じゃあ、Java にあるような アノテーションがどうこう みたいな問題とは無縁だね! 11
  11. © LY Corporation Kotlin の null 安全性 • Java と違い、null

    を許容する型 (例: String?) とそうでない型 (例: String) を 型システムレベルで明⽰しているので、静的解析でエラーにできる じゃあ、Java にあるような アノテーションがどうこう みたいな問題とは無縁だね! コトはそう単純ではない! 12
  12. © LY Corporation Platform Type とは? • 問題は、Kotlin から Java

    の関数を呼び出すときに発⽣する public String find() { return System.getProperty("x"); } val s = api.find() s.length Java Kotlin Kotlin からすると、find の返り値が null かそうでないかわからず、null かどうか 不明な Platform Type として解釈される (s の型は String!) 13 null かどうかわからないので null チェックも強制されない もし s の値が null だったらここで NPE が発⽣してしまう
  13. © LY Corporation IDE での表⽰を確認 14 ホバーすると String! であることはわかるが 特に

    IDE で警告はなく、 コンパイルエラーも出ない Java Kotlin 実⾏時 実⾏時には NPE が⽣じる
  14. © LY Corporation Annotation をつけてみると? 15 • JSpecify の @Nullable

    annotation がついているとどうなる? public @Nullable String find() { return System.getProperty("x"); } val s = api.find() s.length Java Kotlin アノテーションのおかげで s の型は String? になる コンパイルエラーになり、 null ハンドリングが強制される
  15. © LY Corporation IDE での表⽰を確認 16 @Nullable アノテーション付与済み Java Kotlin

    コンパイルエラーになり、 null ハンドリングが強制される
  16. © LY Corporation Kotlin から解釈できる annotation 17 • Kotlin 1.1.50

    でコンパイルオプション -Xjsr305=strict が登場 *1 • これを指定すると、@Nullable のついた変数の null ハンドリングが強制される (コンパイルエラーになる) • Kotlin 1.5.20 からは JSpecify のサポート開始 *2 • 2.0.20 からは JSpecify の全てのアノテーションを正しく解釈できるようになった • 当初はデフォルトでは warn 扱いだったが、Kotlin 2.1 からはエラー扱いに • コンパイラオプション -Xnullability-annotations で挙動を変更できる *3 • 現在解釈できる annotation は上記含む 9 種類 *4 *1: https://blog.jetbrains.com/kotlin/2017/09/kotlin-1-1-50-is-out/ *2: https://kotlinlang.org/docs/whatsnew1520.html#support-for-jspecify-nullness-annotations *3: https://jspecify.dev/docs/whether/#kotlin *4: https://kotlinlang.org/docs/java-interop.html#nullability-annotations
  17. © LY Corporation Spring Boot 4 時代の nullability annotation 18

    • Spring Boot 3.x は 2026 年 6 ⽉に EOL なので Spring Boot 4 を前提とすると…… • Spring Boot 4 では *1 • Spring の API に JSpecify Annotation 付与済み • Kotlin のベースラインは 2.1 に引き上げ • JSR-305 は事実上の休⽌状態 *2, Spring Framework の annotation も deprecated *3 *1: https://spring.io/blog/2025/12/18/next-level-kotlin-support-in-spring-boot-4 *2:https://jcp.org/en/jsr/detail?id=305 *3: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0.0-M2-Release-Notes 新たに nullability アノテーションを付与するなら JSpecify を使おう! JSR-305, Spring Framework annotation を 使っている⼈は JSpecify に移⾏しよう!
  18. © LY Corporation JSpecify のアノテーションは 4 種類 (1) 20 •

    @Nullable • 型が null 許容である時に使う • フィールド、返り値、引数等に付与できる • @NonNull • 型が null を許容しない時に使う • 付与できる箇所は上と同じ • だが、次に⽰す @NullMarked の⽅を使う ケースが多く、こちらを使う機会は少なそう https://jspecify.dev/docs/user-guide/ static @Nullable String foo(@NonNull String x) { return x.isEmpty() ? null : x; } static @NonNull String bar(@Nullable String x) { return x == null ? "" : x; }
  19. © LY Corporation JSpecify のアノテーションは 4 種類 (2) 21 •

    @NullMarked • モジュール、パッケージ、クラス、メソッド等に 付与でき、対象を⼀括でnonnull 扱いにできる • @Nullable がある場合はそちらが優先 https://jspecify.dev/docs/user-guide/ @NullMarked class Strings { static @Nullable String foo(String x) { return x.isEmpty() ? null : x; } } • @NullUnmarked • NullMarked を打ち消して、何もアノテーションをつけていないのと同じ状態にする • コードベースに対して段階的にアノテーションを導⼊したい時に使⽤できる
  20. © LY Corporation アノテーション付与戦略 22 • NonNull か Nullable かはどうやって判定する?

    • 外部ライブラリの場合、まずは依存ライブラリのドキュメントを読もう • ドキュメントに記載がなく、実装から⼿掛かりを探るには…… • return null; があればそのメソッドの返り値は nullable であろう • もし if (parameter == null) みたいなのがあればそのパラメータは nullable であろう • あるフィールドが null あるいは null になりうる値を代⼊されていたら、その フィールドは nullable であろう https://jspecify.dev/docs/applying/
  21. © LY Corporation アノテーション付与の順序 23 • 基本的には、他のクラスやパッケージへの依存が少ないクラスから始め、 それが終わったら呼び出し元へと外側へ進んでいく 1. null

    になりうるものについて @Nullable を付与していく 1. 付与し終わったら @NullMarked をつける 2. まだ注釈付与が間に合っていない部分があればその部分に @NullUnmarked をつける 2. NullAway などの静的解析ツールによりチェック 3. Kotlin から呼び出してみて、Platform Type が⽣じないか確認 https://jspecify.dev/docs/applying/
  22. © LY Corporation 補⾜: NullAway は何をチェックしてくれる? 24 • アノテーションと実装の齟齬や、アノテーション同⼠の⽭盾を検出 •

    @NonNull と annotate した変数に null を代⼊している • 引数が @Nullable で、null チェックをせずそのまま値を返却しているが、 返り値の値は @NonNull になっている • 変数が @Nullable なのに、null に対して⾏うと NPE が発⽣する操作を ⾏っている • 逆に以下のようなものは検出できない • @NonNull でいいのに @Nullable になっている https://github.com/uber/NullAway/wiki/Error-Messages
  23. © LY Corporation 実際の進め⽅ 25 • 先述の付与戦略を組み込んだ AI Agent ⽤の

    skills を作成 (以下は指⽰の概略) 1. 複数モジュールからなるレポジトリの場合、他のクラスやパッケージへの依存が少ない モジュールを特定しそれから着⼿する 2. すべてのフィールド・パラメータ・返り値について「API 仕様ドキュメントの確認」 「実装コードのシグナル確認」を指⽰ 3. nullability 判定と根拠を表形式でユーザに提⽰し、承認を得る 4. @Nullable アノテーション付与 -> package-info.java を作成し @NullMarked 付与 5. ビルド & テスト (NullAway の警告もここでチェック。必要に応じて修正) 6. 利⽤者への影響を分析。対象モジュールの public API について以下が存在する場合は マイナーバージョンアップ & リリース周知の必要性を報告 a. メソッドの返り値、フィールドの getter の返り値が nullable に確定するケース b. メソッドのパラメータが non-null に確定するケース
  24. © LY Corporation 実際やってみてわかってきたこと 26 • 依存ライブラリのドキュメントに nullability が明⽰されているかによって難易度が変わる •

    API docs がないと、ライブラリの関数の返り値が null になりうるかどうかを ソースコードを読みにいって調べる必要が⽣じる • 当初は外部に露出する公開 API のみアノテーションを付与する⽅針も考えていたが、 結局ソースコード全体に付与するアプローチを採った • 公開 API のメソッドが内部 DTO を返り値に持つ場合が多く、結局それらの nullability を調査することになる • 公開 API に全て @NullMarked をつける / それ以外に @NullUnmarked をつけるの は⾯倒な上つけ忘れを招く • 外部から⾒えなくても、内部実装で NPE が⽣じるのを防げる
  25. © LY Corporation 注意点 (1) 27 • Jackson でデシリアライズされるクラスについて。API 仕様上

    null になり得ない ことを理由にフィールドを @NonNull に設定しておくと、NullAway に怒られる • 理由: NullAway はコンストラクタ終了時にすべての @NonNull フィールドが 初期化されているかを検査するため、setter 経由で後から値が⼊るフィールドに 対して偽陽性の警告が出る • 対処法: non-null フィールド初期化チェックをスキップするためにクラスに @SuppressWarnings(“NullAway.Init”) をつけるか、NullAway の設定 (-XepOpt:NullAway:ExternalInitAnnotations) を利⽤するのが良い https://github.com/uber/NullAway/wiki/Configuration
  26. © LY Corporation 注意点 (2) 28 • JSR-305 annotation から移⾏する場合は

    array のアノテーションに注意を! https://jspecify.dev/docs/using/#if-your-code-already-uses-jsr-305-annotations @Nullable Object[] • JSpecify で array ⾃体を nullable にしたいなら Object @Nullable [] とすべき。 @Nullable の直後に来たものが nullable になると覚えると良い JSR-305: 「array ⾃体が nullable」 JSpecify: 「nullable である object の array」
  27. © LY Corporation 補⾜: アノテーションのつけ間違いに注意 29 • たまに外部ライブラリ側が JSpecify annotation

    をつけ間違えていることが あり、利⽤者側で問題が発⽣ • nullable なものが誤って nonnull と指定され、Kotlin でのコンパイル エラーを招いた例 (reactor-core#4151) • 同様に Spring Security でも誤った nonnull 指定があり、NullAway の 偽陽性が発⽣したため patch (spring-security#19156) を提出するなど • つけ間違いは利⽤者に混乱を招くので注意を!
  28. © LY Corporation Platform Type の存在に気づくのは難しい 31 • IntelliJ IDEA

    上で変数をホバーすれば Platform Type であることはわかるが、 特に Problem にも表⽰されないし、Qodana などの CLI ツールでも引っかからない • Kotlin のリンタである detekt には HasPlatformType というルールがあるが、これも Kotlin の公開 API に platform type が露出している場合しか検出してくれない • というわけで、Kotlin のソースコードの中に潜む Platform Type を検出する detekt のカスタムルールを⾃作しました • https://github.com/ternbusty/detekt-platform-type • detekt 2.0.0-alpha.3 に依存しており、あくまでも experimental なものです
  29. © LY Corporation Platform Type はどうやって作られる? 32 Kotlin ソースコード (.kt)

    PSI Program Structure Interface Kotlin Frontend Compiler FIR Frontend Intermediate Representation .class Java 関数の呼び出しがあった場合 Java の型を Kotlin に変換する処理を⾏う アノテーションがない場合は Platform Type となる IR Kotlin Backend Compiler 型の解決などが⾏われる
  30. © LY Corporation Platform Type はどうやって作られる? 33 Kotlin ソースコード (.kt)

    PSI Program Structure Interface Kotlin Frontend Compiler FIR Frontend Intermediate Representation .class IR Kotlin Backend Compiler detekt (Full Analysis Mode) PSI・FIR にアクセスし、 Platform Type を検出できる
  31. © LY Corporation Rule 1: PlatformTypeAudit 34 • Platform Type

    が検出され次第問答無⽤でアラートする Rule • Kotlin コード側で正しい null ハンドリングをしていても検出される • Kotlin のコード修正ではなく、依存先 Java レポジトリのアノテーション付与の必要性を 判断するためのものなので、Kotlin 側の CI 等で⽤いるのは避けた⽅が良い Platform Type を 検知してエラーに Kotlin 側で安全な ハンドルをしていても エラーになる仕様
  32. © LY Corporation Rule 2: PlatformTypeUnhandled 35 • Platform Type

    を正しくハンドリングしていない場合のみアラートする Rule • 検出した Platform Type のうち、正しくハンドルされておらず、NPE を引き起こす可能性が あるもののみ報告する • Kotlin 側のコードが、正しく Platform Type をハンドルできているかを知るための Rule なので、Kotlin 側の CI に⼊れても良い • 以下のように正しく handle しているものについては silence される val withFallback: String = javaClass.getNullableString() ?: "default" val safeStr: String? = javaClass.getNullableString() String? に格納している エルビス演算⼦を使って ハンドリング
  33. © LY Corporation • 特にコンパイル時の挙動や実⾏時の挙動・ パフォーマンスに影響はなし • ただし、アプリケーション側でのメリット として、ライブラリ側が Null

    を返すか 否かが明確になるので、 • JSpecify を解釈できる IDE で、 静的解析の性能が上がる • NullAway などの静的解析ツールを 導⼊していた場合、正確な結果が 出るようになる • Platform Type がなくなり、 null ハンド リングが必要な部分はコンパイル時に きちんとハンドリングが要求されるように なるのがメリット • 逆にハンドリングをしないと、 Kotlin 2.1 以降ではコンパイル エラーになるので注意 • エラーにならないようにコンパイル オプションで変えることはできるので、 アプリケーション側で即座の実装改修が 必要とされるわけではない Java Kotlin 37 結局 JSpecify アノテーションをつけるとどうなる?
  34. © LY Corporation Take Home Message 38 • Java 側のコードにアノテーションを付与すると、Kotlin

    側での Platform Type が 消え、Java ライブラリを安全に利⽤できるようになる • Kotlin だけでなく、そのライブラリを使う Java のアプリケーションにも恩恵がある • Spring Boot 4 時代なので JSpecify のアノテーションを使うべき • detekt カスタムルールを利⽤すれば、Java 側のアノテーション付与の必要性や、 Kotlin 側でのハンドリング不⾜が判定できる