kdnakt blog

hello there.

KotlinのGradleプロジェクトにJavaコードを同居させる

何となく実験してみたのでメモ。

[やりたいこと]

Kotlinのプロジェクトで、Javaのライブラリやクラスを呼び出すというのはよくある。公式ドキュメントでも相互運用性の話が書かれている。

kotlinlang.org

import java.util.ArrayList

fun main() {
    val list = ArrayList<Int>()
    list.add(0)
    println("list=${list}") // list=[0]

    list[0] = 1
    println("list=${list}") // list=[1]
}

反対に、JavaからKotlinのコードを呼び出すこともできる。その場合、例えばKotlinのファイル名がMyObject.ktだった場合、Java側で呼び出す際はMyObjectKtというクラス名になる。

// app.kt
package org.example

fun getTime() { /*...*/ }

// App.java
public static void main(String[] args) {
    System.out.println(org.example.AppKt.getTime());
}

ここまでは以前Kotlinを勉強した際に知っていたのだが、同じプロジェクト内にKotlinのソースコードJavaのプロジェクトを同居させるのはできるのか?と思って気になって調べたら、ちゃんと公式ドキュメントに答えが書いてあった。

[やってみた]

公式ドキュメントによると、単にsrc/main/kotlinというディレクトリの横にsrc/main/javaというディレクトリを作って、Javaのコードを置いておけば良いらしい。

kotlinlang.org

というわけで実際にプロジェクトを作って試してみる。

$ mkdir kotlin-and-java
$ cd kotlin-and-java
$ gradle init
Starting a Gradle Daemon (subsequent builds will be faster)

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 4

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 2

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Kotlin) [1..2] 2

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] 
Project name (default: kotlin-and-java): 
Source package (default: kotlin.and.java): com.kdnakt.kotlin.and.java

> Task :init
Get more help with your project: https://docs.gradle.org/7.5.1/samples/sample_building_kotlin_applications_multi_project.html

BUILD SUCCESSFUL in 47s
2 actionable tasks: 2 executed

これで以下のようなKotlinのコードが自動的に実装される。

// kotlin-and-java/app/src/main/kotlin/com/kdnakt/kotlin/and/java/app/App.kt

package com.kdnakt.kotlin.and.java.app

import com.kdnakt.kotlin.and.java.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))
}

この横に、MyObjectというJavaクラスを作ってみる。Kotlin側で出力結果を確認しやすいように、toString()をオーバーライドしておく。

// kotlin-and-java/app/src/main/java/com/kdnakt/java/MyObject.java
package com.kdnakt.java;

public class MyObject {
    private String name;
    private String value;
    public MyObject(String name, String value) {
        this.name = name;
        this.value = value;
    }

    @Override
    public String toString() {
        return "MyObject{" +
                "name='" + name + '\'' +
                ", value='" + value + '\'' +
                '}';
    }
}

ファイル階層は次のようになる。

Javaで実装したMyObjectクラスをKotlin側で呼び出すと、以下のようになる。

// kotlin-and-java/app/src/main/kotlin/com/kdnakt/kotlin/and/java/app/App.kt

package com.kdnakt.kotlin.and.java.app

import com.kdnakt.java.MyObject

fun main() {
    // Javaのクラスを呼び出す
    val obj = MyObject("key", "foo")
    println(obj)
    // MyObject{name='key', value='foo'}
}

同じプロジェクトのKotlinのコードからJavaのクラスを問題なく呼び出すことができた。

[まとめ]

  • KotlinとJavaは相互運用性が担保されていて、お互いの実装を呼び出すことが可能
  • 同一のGradleプロジェクト内にKotlinとJavaのコードがあっても問題ない
  • 今回のサンプルコードはこちらのリポジトリにある

github.com