The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Kotlinの使用

Kotlin は、JVMをターゲットにした(他の環境を含めても)非常に人気のあるプログラミング言語です。Kotlinはここ数年で人気が急上昇し、Javaを除いて最も人気のあるJVM言語となっています。

このガイドで説明するように、QuarkusはKotlinを使用するためのファーストクラスのサポートを提供しています。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.6

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

注:Gradleプロジェクトのセットアップについては、以下を参照してください。また、詳細については、 Gradleセットアップページのガイドを参照してください。

Mavenプロジェクトの作成

まず、新しいKotlinプロジェクトが必要です。これは以下のコマンドで行うことができます。

コマンドラインインタフェース
quarkus create app org.acme:rest-kotlin-quickstart \
    --extension='kotlin,resteasy-reactive-jackson'
cd rest-kotlin-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.7.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=rest-kotlin-quickstart \
    -Dextensions='kotlin,resteasy-reactive-jackson'
cd rest-kotlin-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=rest-kotlin-quickstart"

kotlin をエクステンションリストに追加すると、Maven プラグインは、Kotlin で動作するように適切に構成されたプロジェクトを生成します。さらに、 org.acme.ReactiveGreetingResource クラスは Kotlin のソースコードとして実装されます (生成されたテストも同様です)。エクステンションリストに resteasy-reactive-jackson を追加すると、RESTEasy Reactive および Jackson エクステンションがインポートされます。

ReactiveGreetingResource は次のようになります:

ReactiveGreetingResource.kt
package org.acme

import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.Produces
import jakarta.ws.rs.core.MediaType

@Path("/hello")
class ReactiveGreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    fun hello() = "Hello from RESTEasy Reactive"
}

コードの更新

Kotlin のより実用的な使用例を示すために、 Greeting というシンプルな データクラス を追加してみましょう:

Greeting.kt
package org.acme.rest

data class Greeting(val message: String = "")

また、 ReactiveGreetingResource を次のように更新します:

import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.core.MediaType

@Path("/hello")
class ReactiveGreetingResource {

    @GET
    fun hello() = Greeting("hello")
}

これらの変更により、 /hello エンドポイントは、単純な文字列ではなくJSONオブジェクトで応答するようになりました。

テストをパスするためには、 ReactiveGreetingResourceTest を次のように更新します:

import org.hamcrest.Matchers.equalTo

@QuarkusTest
class ReactiveGreetingResourceTest {

    @Test
    fun testHelloEndpoint() {
        given()
          .`when`().get("/hello")
          .then()
             .statusCode(200)
             .body("message", equalTo("hello"))
    }

}

Kotlin バージョン

QuarkusのKotlinエクステンションは、すでに kotlin-stdlib-jdk8kotlin-reflect などの一部のKotlinベースライブラリへの依存を宣言しています。これらの依存関係のKotlinバージョンは、Quarkus BOMで宣言されており、現在のバージョンは1.9.22です。したがって、他のKotlinライブラリには、同じKotlinバージョンを使用することをお勧めします。他のベースKotlinライブラリに依存性を追加する場合(たとえば、 kotlin-test-junit5 )、Quarkus BOMに Kotlin BOM が含まれているため、バージョンを指定する必要はありません。

そうは言っても、使用する Kotlin コンパイラのバージョンを指定する必要があります。 繰り返しになりますが、Quarkus が Kotlin ライブラリに使用するのと同じバージョンを使用することをお勧めします。

通常、Quarkus アプリケーションで別の Kotlin バージョンを使用することはお勧めしません。 ただしそうする必要がある場合は、Quarkus BOM の 前に Kotlin BOM をインポートする必要があります。

重要なMavenの設定ポイント

生成された pom.xml には、Kotlin が選択されていない場合と比較して、以下の修正が含まれています。

  • quarkus-kotlin アーティファクトが依存関係に追加されています。このアーティファクトはライブリロードモードでの Kotlin のサポートを提供します (これについては後ほど説明します)。

  • kotlin-stdlib-jdk8 も依存関係として追加されています。

  • Maven の sourceDirectorytestSourceDirectory ビルドプロパティーは、Kotlin ソースを指すように設定されています (それぞれ src/main/kotlinsrc/test/kotlin )。

  • kotlin-maven-plugin は以下のように設定されています。

pom.xml
<plugin>
    <artifactId>kotlin-maven-plugin</artifactId>
    <groupId>org.jetbrains.kotlin</groupId>
    <version>${kotlin.version}</version>
    <executions>
        <execution>
            <id>compile</id>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>test-compile</id>
            <goals>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <compilerPlugins>
            <plugin>all-open</plugin> (1)
        </compilerPlugins>

        <pluginOptions>
            <!-- Each annotation is placed on its own line -->
            <option>all-open:annotation=jakarta.ws.rs.Path</option>
        </pluginOptions>
    </configuration>

    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-allopen</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
</plugin>
1 all-open アノテーションプラグインの有効化(以下の議論を参照)

注意すべき重要なこととして、 all-open Kotlinコンパイラーのプラグインを使用していることです。なぜこのプラグインが必要なのかを理解するためには、まず、Kotlin コンパイラから生成されるクラスはデフォルトで final でマークされていることに注目してください。

しかし、 final クラスであることは、 ダイナミックプロキシー を作成する必要がある様々なフレームワークではうまく機能しません。

そのため、 all-open Kotlinコンパイラ・プラグインでは、特定のアノテーションを持つクラスを final としてマーク *しない * ようにコンパイラを設定することができます。上記のスニペットでは、 jakarta.ws.rs.Path のアノテーションを持つクラスは final としないように指定しています。

例えば、 jakarta.enterprise.context.ApplicationScoped でアノテーションされた Kotlin クラスがアプリケーションに含まれている場合、 <option>all-open:annotation=jakarta.enterprise.context.ApplicationScoped</option> も追加する必要があります。JPAエンティティクラスのように、実行時に動的プロキシを作成する必要があるクラスも同様です。

Quarkusの将来のバージョンでは、この設定を変更する必要がないようにKotlinコンパイラプラグインを設定するようになる予定です。

重要なGradle設定ポイント

Mavenの設定と同様、Gradleを使用する場合、Kotlinを選択すると以下のような修正が必要になります:

  • quarkus-kotlin アーティファクトが依存関係に追加されています。このアーティファクトはライブリロードモードでの Kotlin のサポートを提供します (これについては後ほど説明します)。

  • kotlin-stdlib-jdk8 も依存関係として追加されています。

  • Kotlin プラグインが有効になり、暗黙のうちに sourceDirectorytestSourceDirectory のビルドプロパティーが追加され、Kotlin ソース (それぞれ src/main/kotlinsrc/test/kotlin ) を指すようになります。

  • all-open Kotlinプラグインは、アノテーションがハイライトされているクラスを最終的なものとしてマークしないようにコンパイラに指示します (必要に応じてカスタマイズしてください)

  • ネイティブイメージを使用する場合は、http (または https) プロトコルの使用を宣言しなければなりません。

  • 以下に設定例を示します:

plugins {
    id 'java'
    id 'io.quarkus'

    id "org.jetbrains.kotlin.jvm" version "1.9.22" (1)
    id "org.jetbrains.kotlin.plugin.allopen" version "1.9.22" (1)
}

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22'

   implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")

    implementation 'io.quarkus:quarkus-resteasy-reactive'
    implementation 'io.quarkus:quarkus-resteasy-reactive-jackson'
    implementation 'io.quarkus:quarkus-kotlin'

    testImplementation 'io.quarkus:quarkus-junit5'
    testImplementation 'io.rest-assured:rest-assured'
}

group = '...' // set your group
version = '1.0.0-SNAPSHOT'

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

allOpen { (2)
    annotation("jakarta.ws.rs.Path")
    annotation("jakarta.enterprise.context.ApplicationScoped")
    annotation("jakarta.persistence.Entity")
    annotation("io.quarkus.test.junit.QuarkusTest")
}

compileKotlin {
    kotlinOptions.jvmTarget = JavaVersion.VERSION_11
    kotlinOptions.javaParameters = true
}

compileTestKotlin {
    kotlinOptions.jvmTarget = JavaVersion.VERSION_11
}
1 Kotlinプラグインのバージョンを指定する必要があります。
2 上記のMavenガイドにあるように、all-open設定が必要です。

または、GradleのKotlin DSLを使用している場合:

plugins {
    kotlin("jvm") version "1.9.22" (1)
    kotlin("plugin.allopen") version "1.9.22"
    id("io.quarkus")
}

repositories {
    mavenLocal()
    mavenCentral()
}

val quarkusPlatformGroupId: String by project
val quarkusPlatformArtifactId: String by project
val quarkusPlatformVersion: String by project

group = "..."
version = "1.0.0-SNAPSHOT"


repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))

    implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))

    implementation("io.quarkus:quarkus-kotlin")
    implementation("io.quarkus:quarkus-resteasy-reactive")
    implementation("io.quarkus:quarkus-resteasy-reactive-jackson")

    testImplementation("io.quarkus:quarkus-junit5")
    testImplementation("io.rest-assured:rest-assured")
}

group = '...' // set your group
version = "1.0.0-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

allOpen { (2)
    annotation("jakarta.ws.rs.Path")
    annotation("jakarta.enterprise.context.ApplicationScoped")
    annotation("jakarta.persistence.Entity")
    annotation("io.quarkus.test.junit.QuarkusTest")
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
    kotlinOptions.jvmTarget = JavaVersion.VERSION_11.toString()
    kotlinOptions.javaParameters = true
}
1 Kotlinプラグインのバージョンを指定する必要があります。
2 上記のMavenガイドにあるように、all-open設定が必要です。

Quarkus BOMのKotlin versionをオーバーライドする(Gradle)

アプリケーションで Quarkus の BOM で指定されているものとは異なるバージョンを使用する場合 (たとえば、プレリリース機能を試すため、または互換性の理由から)、Gradle の依存関係で strictly {} バージョン修飾子を使用してこれを行うことができます 。 例:

plugins {
    id("io.quarkus")
    kotlin("jvm") version "1.7.0-Beta"
    kotlin("plugin.allopen") version "1.7.0-Beta"
}

configurations.all {
    resolutionStrategy {
        force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0-Beta"
        force "org.jetbrains.kotlin:kotlin-reflect:1.7.0-Beta"
    }
}

ライブ・リロード

Quarkusは、ソースコードに加えられた変更をライブでリロードする機能をサポートしています。このサポートはKotlinでも利用できます。つまり、開発者はKotlinのソースコードを更新して、変更が反映されたことをすぐに確認することができます。

この機能の動作を確認するには、まず次のコマンドを実行します:

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

http://localhost:8080/hello に対してHTTP GETリクエストを実行すると、 message フィールドに hello という値を持つJSONメッセージが表示されます。

ここで、お好きなエディタやIDEを使って、 ReactiveGreetingResource.kt をアップデートし、 hello のメソッドを以下のように変更します:

fun hello() = Greeting("hi")

http://localhost:8080/hello に対してHTTP GETリクエストを実行すると、 message フィールドに hi という値を持つJSONメッセージが表示されるはずです。

注意点としては、お互いに依存関係にあるJavaとKotlinの両方のソースに変更を加えた場合、ライブリロード機能が利用できないということです。将来的にはこの制限を緩和したいと考えています。

ライブリロードコンパイラーの設定

開発モードで kotlinc によって使用されるコンパイラ フラグをカスタマイズする必要がある場合は、それらを quarkus プラグインで設定できます。

Maven
<plugin>
  <groupId>${quarkus.platform.group-id}</groupId>
  <artifactId>quarkus-maven-plugin</artifactId>
  <version>${quarkus.platform.version}</version>

  <configuration>
    <source>${maven.compiler.source}</source>
    <target>${maven.compiler.target}</target>
    <compilerOptions>
      <compiler>
        <name>kotlin</name>
        <args>
          <arg>-Werror</arg>
        </args>
      </compiler>
    </compilerOptions>
  </configuration>

  ...
</plugin>
Gradle (Groovy DSL)
quarkusDev {
    compilerOptions {
        compiler("kotlin").args(['-Werror'])
    }
}
Gradle (Kotlin DSL)
tasks.quarkusDev {
     compilerOptions {
        compiler("kotlin").args(["-Werror"])
    }
}

アプリケーションのパッケージング

いつものように、アプリケーションは以下の方法でパッケージ化されます:

コマンドラインインタフェース
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

そして次のコマンドで実行できます。 java -jar target/quarkus-app/quarkus-run.jar.

ネイティブ実行可能ファイルを次のようにビルドすることもできます:

コマンドラインインタフェース
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native

Kotlin と Jackson

com.fasterxml.jackson.module:jackson-module-kotlin の依存関係と quarkus-jackson エクステンション(または quarkus-resteasy-jacksonquarkus-resteasy-reactive-jackson エクステンションのいずれか)がプロジェクトに追加されている場合、Quarkus は KotlinModule から ObjectMapper の Bean を自動的に登録します(詳細は このガイドを参照してください)。

Kotlin のデータクラスを native-image で使用すると、Kotlin Jackson Module が登録されているにもかかわらず、 JVM バージョンでは発生しないシリアライズエラーが発生することがあります。これは、より複雑なJSON階層を持っている場合に特に見られる現象で、下位のノードで問題が発生するとシリアライズに失敗します。表示されるエラーメッセージはキャッチオールで、通常はルートオブジェクトの問題を表示しますが、必ずしもそうではない場合もあります。

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `Address` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

native-image との完全な互換性を確保するためには、Jackson @field:JsonProperty("fieldName") アノテーションを適用し、以下の図のように nullable のデフォルトを設定することをお勧めします。Intellijのプラグイン(JSON to Kotlin Classなど)を使用して、サンプルのJSONに対するKotlinデータクラスの生成を自動化し、Jacksonアノテーションを有効にして、自動コード生成の一部としてnullableパラメータを選択することができます。

import com.fasterxml.jackson.annotation.JsonProperty

data class Response(
	@field:JsonProperty("chart")
	val chart: ChartData? = null
)

data class ChartData(
	@field:JsonProperty("result")
	val result: List<ResultItem?>? = null,

	@field:JsonProperty("error")
	val error: Any? = null
)

data class ResultItem(
	@field:JsonProperty("meta")
	val meta: Meta? = null,

	@field:JsonProperty("indicators")
	val indicators: IndicatorItems? = null,

	@field:JsonProperty("timestamp")
	val timestamp: List<Int?>? = null
)

...

KotlinとKubernetesクライアント

quarkus-kubernetes エクステンションを使用して、Kotlin クラスを CustomResource 定義にバインドする場合(オペレーターを構築する場合など)、基盤となる Fabric8 Kubernetes Client が独自の静的な Jackson ObjectMapper を使用することに注意する必要があります。これは KotlinModule を使用して以下のように設定できます:

import io.fabric8.kubernetes.client.utils.Serialization
import com.fasterxml.jackson.module.kotlin.KotlinModule

...
val kotlinModule = KotlinModule.Builder().build()
Serialization.jsonMapper().registerModule(kotlinModule)
Serialization.yamlMapper().registerModule(kotlinModule)

ネイティブイメージへのコンパイル時には慎重にテストし、問題が発生した場合はJava互換のJacksonバインディングにフォールバックしてください。

コルーチンのサポート

エクステンション

以下のエクステンションは、Kotlinのコルーチンをサポートするために、メソッドシグネチャでKotlinの suspend キーワードを使用できるようにするものです。

エクステンション Comments

quarkus-resteasy-reactive

Jakarta REST Resource Methodsへのサポートが提供されます

quarkus-rest-client-reactive

RESTクライアントインタフェースメソッドへのサポートが提供されます

quarkus-smallrye-reactive-messaging

Reactiveメッセージングメソッドへのサポートが提供されます

quarkus-scheduler

スケジューラーメソッドへのサポートが提供されます

quarkus-smallrye-fault-tolerance

宣言型アノテーションベースAPIへのサポートが提供されます

Kotlin coroutines と Mutiny

Kotlinのコルーチンは、非同期でリアクティブな方法で実際に実行される命令型プログラミングモデルを提供します。MutinyとKotlinの相互運用を簡単にするために、モジュール io.smallrye.reactive:mutiny-kotlin が存在し、 ここ で解説されています。

Kotlinでの CDI @Inject

Kotlinのリフレクションアノテーション処理はJavaとは異なります。CDIの@Injectを使用していると、以下のようなエラーが発生することがあります。"kotlin.UninitializedPropertyAccessException: lateinitプロパティーxxxが初期化されていません"

以下の例では、アノテーションに @field を追加することで、Kotlin のリフレクションアノテーション定義に @Target がない場合に対応し、問題を簡単に解決できます。

import jakarta.inject.Inject
import jakarta.enterprise.inject.Default
import jakarta.enterprise.context.ApplicationScoped

import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.Produces
import jakarta.ws.rs.core.MediaType



@ApplicationScoped
class GreetingService {

    fun greeting(name: String): String {
        return "hello $name"
    }

}

@Path("/")
class ReactiveGreetingResource {

    @Inject
    @field: Default (1)
    lateinit var service: GreetingService

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/hello/{name}")
    fun greeting(name: String): String {
        return service.greeting(name)
    }

}
1 Kotlinでは、アノテーション定義に@Targetがないため、@field: xxx修飾子が必要です。この例では @field: xxx を追加します。@Defaultが修飾子として使用され、デフォルトBeanの使用を明示的に指定します。

あるいは、Javaの例を変更せずに動作し、テスト性が高く、Kotlinのプログラミングスタイルに最も適しているコンストラクタ・インジェクションの使用をお勧めします。

import jakarta.enterprise.context.ApplicationScoped

import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.Produces
import jakarta.ws.rs.core.MediaType

@ApplicationScoped
class GreetingService {
    fun greeting(name: String): String {
        return "hello $name"
    }
}

@Path("/")
class ReactiveGreetingResource(
    private val service: GreetingService
) {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/hello/{name}")
    fun greeting(name: String): String {
        return service.greeting(name)
    }

}

関連コンテンツ