前回のつづき。
[Gradleで依存関係を宣言する]
Gradleで、あるプロジェクトが利用する依存関係にあるプロジェクトやライブラリを宣言する方法として、api()とimplementation()の2つがある(他にもtestImplementation()とかruntimeOnly()とかたくさんあるけど省略)。
前回参考にしたこちらのマルチプロジェクト構成を参考に見ていく。
utilitiesサブプロジェクトの依存関係の定義は以下のようapi()を利用している。
multi-project-sample/utilities/build.gradle.kts
dependencies { api(project(":list")) }
utilitiesサブプロジェクトでは、listサブプロジェクトで定義されているLinkedListクラスを利用して、以下のような関数が定義されている。
package multi.project.sample.utilities // listサブプロジェクトからimport import multi.project.sample.list.LinkedList class StringUtils { companion object { fun join(source: LinkedList): String { return JoinUtils.join(source) } fun split(source: String): LinkedList { return SplitUtils.split(source) } } }
一方、appサブプロジェクトでは依存関係が以下のようにimplementation()で定義されている。
multi-project-sample/app/build.gradle.kts
dependencies { implementation("org.apache.commons:commons-text") implementation(project(":utilities")) }
appサブプロジェクトの中で、main関数を持つApp.ktファイルは以下の通り。
package multi.project.sample.app // utilitiesサブプロジェクトのクラスをimport import multi.project.sample.utilities.StringUtils import org.apache.commons.text.WordUtils fun main() { val tokens = StringUtils.split(MessageUtils.getMessage()) val result = StringUtils.join(tokens) println(WordUtils.capitalize(result)) }
どちらも別のプロジェクトのクラスをimportしているが、依存関係の宣言の仕方がapi()とimplementation()でそれぞれ異なる。
[api()とimplementation()]
api()とimplementation()の違いは、Java Library Pluginのページで説明されていた。
まとめると以下のような違いがある。
- api()
- implementation()
- ライブラリの内部で利用する依存関係を宣言する場合に使う
- ライブラリの利用者には公開されない
先ほどの例に戻って考えてみる。
utilitiesサブプロジェクトが公開するAPIの戻り値の型が、listサブプロジェクトで定義されているLinkedListクラスになっているから、utilitiesサブプロジェクトではlistサブプロジェクトへの依存をapi()で定義する。これにより、utilitiesサブプロジェクトの利用者(つまりappサブプロジェクト)のコンパイル・クラスパスにLinkedListクラスを登場させることができる。
他方で、appサブプロジェクトを利用するプロジェクトはないので、サブプロジェクト内部で利用する依存関係のみを宣言すればよい。なので、appサブプロジェクトではimplementation()を利用してutilitiesサブプロジェクトへの依存を定義している。
[implementation()のメリット]
先ほどのJava Library Pluginのページによると、可能な限りimplementation()を利用するのが好ましいとされていた。
以下のようなメリットがあるらしい。
- 遷移的な依存関係に誤って依存しなくなる
- 利用者側のクラスパスが少なくなるのでコンパイルが速くなる
- implementation()の依存関係が変わってもライブラリ利用者側は再コンパイルが不要
- maven-publishプラグインを利用した時に、pom.xmlでの依存関係のscopeが自動的にruntime/compileで設定される
これまで特に学ぶこともなく、雰囲気でmavenを利用していた。そのため、依存関係のscopeはほとんど意識せずデフォルトのcompileスコープを利用するか、JUnitなどでたまにtestスコープを選択する程度だった。
コンパイルが多少でも速くなるなら、真面目にmavenでもスコープ設定してみようかな、と思ったけどライブラリ作ることなんてそんなにないから関係ないか……。
[Gradle7.0未満]
Gradleは当初api()/implementation()のかわりに、compile()/runtime()という依存関係の設定方法を提供していた。
しかし、Gradle 3.4でより細かい制御が可能なapi()/implementation()が導入され、2021年4月にリリースされたGradle 7.0でcompile()/runtime()のサポートが打ち切られた。