Kotlin の internal はアクセス修飾子(visibility modifier)の一つで、「同じモジュール内からのみアクセス可能」という可視性を持っています。
Kotlin では、クラス・関数・プロパティなどの可視性を制御するために、次の4つのアクセス修飾子が用意されています。
| 修飾子 | アクセス範囲 |
|---|---|
| public | どこからでもアクセス可能 |
| private | 宣言されたスコープ内のみ |
| protected | クラスおよびサブクラス |
| internal | 同一モジュール内 |
Kotlinでは public がデフォルトです。
そのため、何も指定しない場合は外部からも参照できる公開 API になります。
internal は、その公開範囲を モジュール内に限定するための修飾子です。
モジュールとは何か
internal を理解するうえで最も重要なのは モジュールという概念です。
Kotlinにおけるモジュールとは、一緒にコンパイルされるコードの集合を指します。
開発環境によって具体的な単位は少し異なりますが、代表的な例は次の通りです。
- IntelliJ IDEA / Android Studio の module
- Maven の project
- Gradle の source set
- Kotlin コンパイラで一度にコンパイルされる単位
つまり、同じモジュールに属するコードからは internal 宣言にアクセスできますが、別モジュールからはアクセスできません。
これは Kotlin の モジュールベースのカプセル化を実現する仕組みです。
internal を付けられる対象
internal は Kotlin のさまざまな宣言に付与できます。
主な対象は次の通りです。
- クラス
- 関数
- プロパティ
- コンストラクタ
- オブジェクト
- トップレベル宣言
たとえば、モジュール内部だけで使うユーティリティ関数や実装クラスなどに internal を付けることで、外部からのアクセスを防ぐことができます。
Java のアクセス修飾子との違い
Javaには internal に直接対応する修飾子は存在しません。
よく比較されるのは package-private(デフォルトアクセス)ですが、この2つは似ているようで実際には基準が異なります。
| 言語 | アクセス基準 |
|---|---|
| Java package-private | 同じパッケージ |
| Kotlin internal | 同じモジュール |
Javaは パッケージ単位で可視性を制御しますが、Kotlinは モジュール単位で制御します。
そのため、internal は Java の package-private の完全な代替ではありません。
JVM 上での internal の扱い
Kotlin の internal は、JVM バイトコード上では Java から見ると public として現れます。
ただし、Kotlin コンパイラは一部の宣言に対して 名前マングリング(name mangling)を行います。
これは次のような目的があります。
- Java からの意図しない利用を減らす
- Kotlin の可視性ルールを保つ
- 同名メンバーの衝突を回避する
そのため、Java 側からアクセスできるケースはありますが、Kotlin の公開 API と同じ意味で使うことは想定されていません。
ライブラリ設計での internal の重要性
internal は ライブラリ設計や大規模プロジェクトで特に重要な役割を持ちます。
ソフトウェア設計では、公開 API と内部実装を明確に分けることが重要です。
一般的な構造は次のようになります。
- 公開される API
- 内部の実装
このとき、外部に公開したいインターフェースだけを public にし、実装クラスを internal にすることで、利用者が内部実装に依存するのを防ぐことができます。
これにより、
- API の変更を安全に行える
- 実装を自由にリファクタリングできる
- 外部依存を減らせる
といったメリットがあります。
マルチモジュール構成での活用
internal は、マルチモジュールプロジェクトで非常に効果的です。
たとえば、次のような構成を考えます。
- アプリモジュール
- ドメインモジュール
- データモジュール
この場合、データモジュールの内部実装を internal にしておけば、アプリ側からは直接アクセスできなくなります。
つまり、
- インターフェースだけ公開する
- 実装はモジュール内に隠す
という設計を自然に実現できます。
これは クリーンアーキテクチャやレイヤードアーキテクチャと非常に相性が良い設計です。
inline 関数と internal の関係
Kotlin では inline 関数と可視性の関係に注意が必要です。
public inline 関数は、コンパイル時に呼び出し元へ展開されます。
そのため、関数の内部で private や internal の宣言を直接参照できない場合があります。
この制約を回避するために使われるのが @PublishedApi アノテーションです。
@PublishedApi を付けることで、その internal 宣言を インライン展開時に公開 API として扱えるようにすることができます。
ただし、このアノテーションを付けた宣言は実質的に公開 API に近い扱いになるため、後から変更すると互換性問題が発生する可能性があります。
テストコードとの関係
Gradle プロジェクトでは、main のコードに対してtest source set から internal 宣言へアクセスできる特例があります。
これは Kotlin の設計上、「内部実装もテストできるようにする」という目的で用意された仕組みです。
ただし、この挙動は Gradle の source set 構造に依存する特例であり、一般的に「別モジュールのテストから internal が見える」という意味ではありません。
internal を使うべき場面
実務で internal がよく使われるのは、次のようなケースです。
内部ユーティリティ
モジュール内だけで使う関数やヘルパークラス。
ライブラリの内部実装
公開 API の裏側にある実装コード。
Repository や DataSource の実装
インターフェースは公開し、実装はモジュール内に閉じる。
アーキテクチャ境界の保護
レイヤー外から直接利用されることを防ぐ。
internal の本質
internal の本質は、モジュール単位でカプセル化を実現するための仕組みという点にあります。
Kotlin は Java よりも モジュール設計を強く意識した言語であり、internal はその設計思想を支える重要な要素です。
まとめ
Kotlin の internal は、同一モジュール内からのみアクセスできるアクセス修飾子です。
主な特徴は次の通りです。
- モジュール単位で可視性を制御する
- クラス・関数・プロパティなどに適用できる
- Java には直接対応する修飾子がない
- JVM 上では public として現れるが Kotlin コンパイラが制御する
- ライブラリ設計やマルチモジュール構成で重要
- inline 関数と組み合わせる場合は
@PublishedApiが関係する
適切に internal を使うことで、公開 API と内部実装を明確に分離した安全な設計が可能になります。
以上、Kotlinのinternalについてでした。
最後までお読みいただき、ありがとうございました。










