20190302のKotlinに関する記事は7件です。

「AndroidXでのバックキー制御」について、OnBackPressedCallbackの使い方・注意点を補足

はじめに

@androhiさんの記事「AndroidXでのバックキー制御」を読んで、これは使わない手はない!と思いプロダクトに導入し、その時にハマった点や補足を皆様に共有したいと思い記事にしました。
@androhiさんに許可を頂いてます。ありがとうございます!)

ブログの内容を掻い摘んで説明すると、
Activity#onBackPressed()でやっていたバックキーの制御を OnBackPressedCallbackで出来るようにしてFragmentでのバックキー制御を簡単にしよう!というやつです。

インストール

  • 利用にはandroidxのappcompat:1.1.0とactivity:1.0.0が必要です。まずはこれらをインストールします。
app/build.gradle
dependencies {
    implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
    implementation 'androidx.activity:activity:1.0.0-alpha04'
}
  • 2019/3/2時点の最新はappcompat:1.1.0-alpha02、activity:1.0.0-alpha04ですが、appcompat:1.1.0-alpha02についてはバグがありエラーが出るためalpha01にダウングレードしています。

使い方

  • まずActivityはandroidxのAppCompatActivityを継承させます。
MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {

// 略
SampleFragment.kt
class EndFragment : QuizFragment(), CoroutineScope {
    val mainActivity: MainActivity
        get() = (activity as MainActivity)
    val isOverrideBack = false

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)


        mainActivity.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                if (isOverrideBack) {
                    // NOTE: tureを返すと何もしない。なので例えばpauseメソッドでバックの制御を上書いたり出来る。
                    pause()
                    return true
                }else {
                    // NOTE: falseの場合はfragmentがスタックに積まれていれば1つ戻る。
                    return false
                }
            }
        })
    }

以上です!とてもシンプルで最高ですね。
ただ、注意点としてhandleOnBackPressedでfalseを返す場合、スタックに積まれたflagmentしか戻らないので、Activityを終了すると行った処理を別途書く必要があります。例えば下記のようにする必要があります。

        mainActivity.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                val fm = baseActivity.supportFragmentManager
                val backStackCnt = fm.backStackEntryCount

                // フラグメントの戻り先ある場合は戻る。
                if (backStackCnt == 1) {
                    baseActivity.finish()
                    return true
                }
                return false
            }
        })

最後に

現時点(2019/3/2)ではまだalphaではありますが、前述のバージョンであれば動作も安定しており、コードも非常にシンプルになるのでプロダクトで採用しても問題ないかなと思ってます。気になった方はぜひ使ってみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「AndroidXでのバックキー制御」について、OnBackPressedCallbackの使い方・注意点補足

はじめに

@androhiさんの記事「AndroidXでのバックキー制御」を読んで、これは使わない手はない!と思いプロダクトに導入し、その時にハマった点や補足を皆様に共有したいと思い記事にしました。
@androhiさんに許可を頂いてます。ありがとうございます!)

ブログの内容を掻い摘んで説明すると、
Activity#onBackPressed()でやっていたバックキーの制御を OnBackPressedCallbackで出来るようにしてFragmentでのバックキー制御を簡単にしよう!というやつです。

インストール

  • 利用にはandroidxのappcompat:1.1.0とactivity:1.0.0が必要です。まずはこれらをインストールします。
app/build.gradle
dependencies {
    implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
    implementation 'androidx.activity:activity:1.0.0-alpha04'
}
  • 2019/3/2時点の最新はappcompat:1.1.0-alpha02、activity:1.0.0-alpha04ですが、appcompat:1.1.0-alpha02についてはバグがありエラーが出るためalpha01にダウングレードしています。

使い方

  • まずActivityはandroidxのAppCompatActivityを継承させます。
MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {

// 略
SampleFragment.kt
class EndFragment : QuizFragment(), CoroutineScope {
    val mainActivity: MainActivity
        get() = (activity as MainActivity)
    val isOverrideBack = false

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)


        mainActivity.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                if (isOverrideBack) {
                    // NOTE: tureを返すと何もしない。なので例えばpauseメソッドでバックの制御を上書いたり出来る。
                    pause()
                    return true
                }else {
                    // NOTE: falseの場合はfragmentがスタックに積まれていれば1つ戻る。
                    return false
                }
            }
        })
    }

以上です!とてもシンプルで最高ですね。
ただ、注意点としてhandleOnBackPressedでfalseを返す場合、スタックに積まれたflagmentしか戻らないので、Activityを終了させるといった処理を別途書く必要があります。例えば下記のようにする必要があります。

        mainActivity.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                val fm = baseActivity.supportFragmentManager
                val backStackCnt = fm.backStackEntryCount

                // フラグメントの戻り先ある場合は戻る。
                if (backStackCnt == 1) {
                    baseActivity.finish()
                    return true
                }
                return false
            }
        })

最後に

現時点(2019/3/2)ではまだalphaではありますが、前述のバージョンであれば動作も安定しており、コードも非常にシンプルになるのでプロダクトで採用しても問題ないかなと思ってます。気になった方はぜひ使ってみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「AndroidXでのバックキー制御」について、OnBackPressedCallbackの使い方や注意点補足

はじめに

@androhiさんの記事「AndroidXでのバックキー制御」を読んで、これは使わない手はない!と思いプロダクトに導入し、その時にハマった点や補足を皆様に共有したいと思い記事にしました。
@androhiさんに許可を頂いてます。ありがとうございます!)

ブログの内容を掻い摘んで説明すると、
Activity#onBackPressed()でやっていたバックキーの制御をAndroidXからは OnBackPressedCallbackが使えるようになったので、これを使ってFragmentでの実装を簡単にしよう!というやつです。

インストール

  • 利用にはandroidxのappcompat:1.1.0とactivity:1.0.0が必要です。まずはこれらをインストールします。
app/build.gradle
dependencies {
    implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
    implementation 'androidx.activity:activity:1.0.0-alpha04'
}
  • 2019/3/2時点の最新はappcompat:1.1.0-alpha02、activity:1.0.0-alpha04ですが、appcompat:1.1.0-alpha02についてはバグがありエラーが出るためalpha01にダウングレードしています。

使い方

  • まずActivityはandroidxのAppCompatActivityを継承させます。
MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {

// 略
SampleFragment.kt
class EndFragment : QuizFragment(), CoroutineScope {
    val mainActivity: MainActivity
        get() = (activity as MainActivity)
    val isOverrideBack = false

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)


        mainActivity.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                if (isOverrideBack) {
                    // NOTE: tureを返すと何もしない。なので例えばpauseメソッドでバックの制御を上書いたり出来る。
                    pause()
                    return true
                }else {
                    // NOTE: falseの場合はfragmentがスタックに積まれていれば1つ戻る。
                    return false
                }
            }
        })
    }

以上です!とてもシンプルで最高ですね。
ただ、注意点としてhandleOnBackPressedでfalseを返す場合、スタックに積まれたflagmentしか戻らないので、Activityを終了させるといった処理を別途書く必要があります。
例えばスタックのFlagmentがなくなったらActivityを終了させたいという時は下記のようにする必要があります。

        mainActivity.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                val fm = baseActivity.supportFragmentManager
                val backStackCnt = fm.backStackEntryCount

                // フラグメントの戻り先ある場合は戻る。
                if (backStackCnt == 1) {
                    baseActivity.finish()
                    return true
                }
                return false
            }
        })

最後に

現時点(2019/3/2)ではまだalphaではありますが、前述のバージョンであれば動作も安定しており、コードも非常にシンプルになるのでプロダクトで採用しても問題ないかなと思ってます。気になった方はぜひ使ってみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MockKの使い方

Kotlin向けMockライブラリのMockKの使い方について簡単にまとめます

MockKがどんな意図で作られているかはこちらの記事で作者が語られていますのでご参照ください。

MockKとは

  • MockKはKotlin独自の言語仕様をほぼ網羅しているモックライブラリ
  • Coroutineobject、private関数などにも対応している優れもの
    • 今回はCoroutineには触れませんので、あしからず。。。
  • 公式ドキュメントにすべて書かれていますので、詳細はこちらを見てください

今回は公式ドキュメントの内容からいくつか代表的なもの(自分がよく使うもの)を紹介させていただきます。
内容に不備等ありましたら、やさしくマサカリ投げてくださいm(_ _)m

動作確認環境について

MockKはKotlin全般で利用できるライブラリですが、今回はAndroid Studioを利用しています。
特にAndroid SDKには依存していないユニットテストで書いていますので、他の環境でも動作するとは思います。

  • MockK 1.9
  • Android Studio 3.3.1
  • junit 4.12

MockKの使い方

セットアップ

下記を参考に、ご自身の環境に合わせてインストールしてください。

DSL

DSLとは domain-specific language(ドメイン固有言語)のことで、特定の領域に特化した言語のことです。
書き方は以下のようになります。(公式ドキュメントより)

val car = mockk<Car>()
every { car.drive(Direction.NORTH) } returns Outcome.OK
car.drive(Direction.NORTH) // returns OK
verify { car.drive(Direction.NORTH) }
confirmVerified(car)

これは公式ドキュメントに記載されている通りですが、1つずつ説明すると

  1. val car = mockk<Car>()
    • モックインスタンスを生成する
  2. every { car.drive(Direction.NORTH) } returns Outcome.OK
    • モックインスタンスにパターンを設定
    • この場合は「carインスタンスのdriveメソッドが引数Direction.NORTHで呼ばれた場合はOutcome.OKを返却する」となる
  3. car.drive(Direction.NORTH) // returns OK
    • 実際に呼び出す(Outcome.OKが返却される)
  4. verify { car.drive(Direction.NORTH) }
    • メソッド呼び出しのチェック
    • この場合は「carインスタンスのdriveメソッドが引数Direction.NORTHで呼び出されている」という意味
    • verify(exactly = 1) { car.drive(Direction.NORTH) }というようにexactlyで呼び出されている回数を指定してチェックすることもできる
  5. confirmVerified(car)
    • verifyで設定した全ての検証が完了していることをチェックする
    • 呼び出されていないものがあると例外をスローする
    • 例えば、以下の場合は例外がスローされる(car.drive(Direction.NORTH)をチェックしていないため)
val car = mockk<Car>()

every { car.drive(Direction.NORTH) } returns Outcome.OK
every { car.drive(Direction.SOUTH) } returns Outcome.OK

car.drive(Direction.NORTH) // returns OK
car.drive(Direction.SOUTH) // returns OK

verify {
    car.drive(Direction.SOUTH)
}

confirmVerified(car)

Annotations

MockKでは便利なDI用のアノテーションがあります。
それらを使って宣言することでモックインスタンスの生成が楽になります。
MockKAnnotations.init()を呼び出すことでインジェクションされます。

アノテーションの種類

  • @MockK
    • モックインスタンスとしてインジェクションしたい場合に使用します
  • @RelaxedMockK
    • Relaxedモックインスタンスとしてインジェクションしたい場合に使用します
    • Relaxedモックインスタンスは後述します
  • @SpyK
    • Spy用のインスタンスとしてインジェクションしたい場合に使用します
    • Spyについては後述します。
  • @InjectMockKs
    • 該当オブジェクトのもつ属性に対してインジェクトしたい場合に使用します
    • 具体的には以下のような場合です
class TrafficSystem {
    lateinit var car1: Car
    lateinit var car2: Car
    lateinit var car3: Car
}

@InjectMockKs
var trafficSystem = TrafficSystem()

// trafficSystemの属性car1, car2, car3にモックインスタンスがインジェクションされる
@Before
fun setUp() = MockKAnnotations.init(this)

Spy

Spyはオブジェクトを実際のコードで動かしつつ、メソッドの引数や呼び出し回数、戻り値などを検証するために使用します

val car = spyk<Car>()
car.drive(Direction.NORTH)
verify {
    car.drive(Direction.NORTH)
}
confirmVerified(car)

注意点としては、アノテーションを利用して初期化する際には@Spyk var car = Car()のようにvarかつインスタンス生成する必要があります。
以下のようにすると初期化時に例外がスローされるので注意しましょう。

@Spyk
lateinit var car: Car

// 初期化時に例外がスローされる
@Before
fun setUp() = MockKAnnotations.init(this)

@Test
fun test() {
    car.drive(Direction.NORTH)
}

Relaxed mock

ざっくり言うと、ダミーの値を返すモックを自動で作ってくれます。
下記の場合だと、driveの戻り値のval outcomeOutcome(name = null, ordinal = 0)というような形のダミー値が返却されるようになります。
戻り値の検証を必要としないテストの場合は、モックを実装する手間が省けるので便利です。

enum class Outcome { OK, NG }
enum class Direction { NORTH, SOURTH, WEST, EAST }

interface Car {
    fun drive(direction: Direction): Outcome
}

fun testMockKSample() {
    val car = mockk<Car>(relaxed = true)
    val outcome = car.drive(Direction.NORTH)
    verify { car.drive(Direction.NORTH) }
    confirmVerified(car)
}

注意点としては、ジェネリクスを使った場合はClassCastExceptionが発生してしまいます。

// NG
interface Factory {
    fun <T> create(): T
}

@Test
fun testMockKSample() {
    val factory = mockk<Factory>(relaxed = true)
    val car = factory.create<Car>()  // throw ClassCastException
}

この場合は、everyを利用してモックオブジェクトの実装をしてあげましょう

  • every { factory.create<Car>() } returns Car()

Mock relaxed for functions returning Unit

戻り値がUnitのものだけRelaxed mockしてくれる機能です。
利用方法は以下の3種類です。

  • 関数
    • mockk<Car>(relaxUnitFun = true)
  • Annotations
    • @MockK(relaxUnitFun = true)
  • MockKAnnotations.init
    • MockKAnnotations.init(this, relaxUnitFun = true)

Object Mock

MockKではobjectもモックできます。
モック化にはmockkObject()を利用します。
また、unmockkAllunmockkObjectを使用することでモックの解除もできます。

object MockObj {
    fun add(a: Int, b: Int) = a + b
}
@Before
fun setUp() {
    mockkObject(MockObj)  // これでモックになる
}

@Test
fun testMockKSample() {
    assertEquals(3, MockObj.add(1, 2))
    every { MockObj.add(1, 2) } returns 55
    assertEquals(55, MockObj.add(1, 2))
}

@After
fun tearDown() {
    unmockkAll()  // or unmockkObject(MockObj)
}

Extension functions

拡張関数は3種類のスコープで実装できます

  • class
  • object
  • module

classとobjectの場合はmockkを利用してモック化が可能です。

data class Obj(val value: Int)

class Ext {
    fun Obj.extensionFunc() = value + 5
}

@Test
fun testMockKSample() {
    with(mockk<Ext>()) {
        every {
            Obj(5).extensionFunc()
        } returns 11

        assertEquals(11, Obj(5).extensionFunc())

        verify {
            Obj(5).extensionFunc()
        }
    }
}

また、スコープがモジュール(トップレベル宣言)の場合は以下のように実装します

app/src/main/kotlin/com/hoge/sample/File.kt
package com.hoge.sample

data class Obj(val value: Int)
fun Obj.extensionFunc() = value + 5
app/src/test/kotlin/com/hoge/sample/FileTest.kt
@Test
fun testMockKSample() {
    mockkStatic("com.hoge.sample.FileKt")

    every {
        Obj(5).extensionFunc()
    } returns 11

    assertEquals(11, Obj(5).extensionFunc())

    verify {
        Obj(5).extensionFunc()
    }
}

@JvmNameを利用して上記のFileKtの部分の命名を変えることもできます。

app/src/main/kotlin/com/hoge/sample/File.kt
@file:JvmName("KFile")
package com.hoge.sample

data class Obj(val value: Int)
fun Obj.extensionFunc() = value + 5

実際に色々な拡張関数をモック化しようとすると、予測できないクラス名があったりします。
例えばFile.endsWith()だとmockkStatic("kotlin.io.FilesKt__UtilsKt")のようにする必要があります。

これを調べるにはKotlinのコードを一度バイトコードにしてからJavaのclassファイルを調べるとわかります。
Android Studioの場合は「Tools -> Kotlin -> Show Kotlin Bytecode」を選択後、Javaにデコンパイルするとわかります。

Private functions mocking

privateな関数をモックするには、モックインスタンスを生成する際にrecordPrivateCallsの引数にtrueを渡すと実装することができます。
具体的には以下の例を参照してください。
今回はdrive()の処理はそのまま動かしたいのでspykを利用しています。

class Car {
    fun drive() = accelerate()

    private fun accelerate() = "going faster"
}

@Test
fun testMockKSample() {
    val mock = spyk<Car>(recordPrivateCalls = true)

    every { mock["accelerate"]() } returns "going not so fast"

    assertEquals("going not so fast", mock.drive())

    verifySequence {
        mock.drive()
        mock["accelerate"]()
    }
}

最後に

MockKにはまだまだたくさんの機能があります。
全部を紹介するとかなりのボリュームになってしまうので、自分がよく使うものを紹介させていただきました。

このライブラリだけで、ここまで機能がそろっていると安心してテストがかけますね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin】List をコピーする / to と as の違い

List をコピーする

List などのコレクション系のクラスには、toListtoSet などの、他の型に変換する拡張関数があります。
これらは変換後の型が変換前の型と同じであっても新しいインスタンスを生成するため、
インスタンスのコピーにも使えます。

val list = listOf('A', 'B')
val copiedList = list.toList()
println("list: $list") // > list: [A, B]
println("copiedList: $copiedList") // > copiedList: [A, B]

val mutableList = mutableListOf('A', 'B')
val copiedMutableList = mutableList.toMutableList()
println("mutableList: $mutableList") // > mutableList: [A, B]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

mutableList += 'C' // コピー元を変更してもコピー先は影響を受けない。
println("mutableList: $mutableList") // > mutableList: [A, B, C]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

toas の違い

toListtoSet などの to 系の関数は元のオブジェクトとは独立したオブジェクトを生成します。
元のオブジェクトを変更しても生成されたオブジェクトは影響を受けません。

部分再掲:

val mutableList = mutableListOf('A', 'B')
val copiedMutableList = mutableList.toMutableList()
println("mutableList: $mutableList") // > mutableList: [A, B]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

mutableList += 'C' // コピー元を変更してもコピー先は影響を受けない。
println("mutableList: $mutableList") // > mutableList: [A, B, C]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

一方、asIterableasSequence などの as 系の関数が生成するオブジェクトは元のオブジェクトが変更されるとその影響を受けます。

val mutableList = mutableListOf('A', 'B')
val reversed = mutableList.asReversed()
println("mutableList: $mutableList") // > mutableList: [A, B]
println("reversed: $reversed") // > reversed: [B, A]

mutableList += 'C' // 生成元を変更すると生成先が影響を受ける。
println("mutableList: $mutableList") // > mutableList: [A, B, C]
println("reversed: $reversed") // > reversed: [C, B, A]

to 系で生成したオブジェクトは生成元の変更の影響を受けないため安全に扱えます。
しかし生成されたオブジェクトも全ての要素の値・参照を持つため、メモリを喰いますし、生成に時間がかかります。

as 系で生成したオブジェクトは生成元の変更に追従するため、追従する必要がないときに使うと思わぬバグを生むことになります。
しかし軽量です。

違いを理解して適切に使い分けましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin】List をコピーする / to〜 と as〜 の違い

List をコピーする

List などのコレクション系のクラスには、toListtoSet などの、他の型に変換する拡張関数があります。
これらは変換後の型が変換前の型と同じであっても新しいインスタンスを生成するため、
インスタンスのコピーにも使えます。

val list = listOf('A', 'B')
val copiedList = list.toList()
println("list: $list") // > list: [A, B]
println("copiedList: $copiedList") // > copiedList: [A, B]

val mutableList = mutableListOf('A', 'B')
val copiedMutableList = mutableList.toMutableList()
println("mutableList: $mutableList") // > mutableList: [A, B]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

mutableList += 'C' // コピー元を変更してもコピー先は影響を受けない。
println("mutableList: $mutableList") // > mutableList: [A, B, C]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

to〜as〜 の違い

toListtoSet などの to〜 系の関数は元のオブジェクトとは独立したオブジェクトを生成します。
元のオブジェクトを変更しても生成されたオブジェクトは影響を受けません。

部分再掲:

val mutableList = mutableListOf('A', 'B')
val copiedMutableList = mutableList.toMutableList()
println("mutableList: $mutableList") // > mutableList: [A, B]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

mutableList += 'C' // コピー元を変更してもコピー先は影響を受けない。
println("mutableList: $mutableList") // > mutableList: [A, B, C]
println("copiedMutableList: $copiedMutableList") // > copiedMutableList: [A, B]

一方、asIterableasSequence などの as〜 系の関数が生成するオブジェクトは元のオブジェクトが変更されるとその影響を受けます。

val mutableList = mutableListOf('A', 'B')
val reversed = mutableList.asReversed()
println("mutableList: $mutableList") // > mutableList: [A, B]
println("reversed: $reversed") // > reversed: [B, A]

mutableList += 'C' // 生成元を変更すると生成先が影響を受ける。
println("mutableList: $mutableList") // > mutableList: [A, B, C]
println("reversed: $reversed") // > reversed: [C, B, A]

to〜 系で生成したオブジェクトは生成元の変更の影響を受けないため安全に扱えます。
しかし生成されたオブジェクトも全ての要素の値・参照を持つため、メモリを喰いますし、生成に時間がかかります。

as〜 系で生成したオブジェクトは生成元の変更に追従するため、追従する必要がないときに使うと思わぬバグを生むことになります。
しかし軽量です。

違いを理解して適切に使い分けましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SpringBoot】Kotlin DSLでビルドしてハロワするまで

やること

以下の資料を参考にしながらSpringBootをKotlin-DSLで動かします。
build.gradle.ktsに移行しよう - DroidKaigi 2019

この記事で使ったリポジトリは以下です。
k163377/kts-test: Spring Boot on Kotlin DSL

感想

ググれば案外情報が出てくるので、調べれば気合で色々なんとかなりました。

手順

以下の手順で進めていきます。

  1. Spring Initializrによる初期化
  2. settings.gradleの書き換え
  3. build.gradleの書き換え
  4. コントローラーを追加してハロワ

環境はIntelliJ IDEA 2018.3.2 (Ultimate Edition)でやりました。

Spring Initializrによる初期化

解説記事は他に沢山あるのでやりません。
今回は「Generate a Gradle Project with Kotlin and Spring Boot 2.1.3」で出力しました。

settings.gradleの書き換え

セッションの5:30辺りを参考に、settings.gradleの書き換えを行います。

settings.gradle(書き換え前)
pluginManagement {
    repositories {
        gradlePluginPortal()
    }
}
rootProject.name = 'kts-test'

'"に書き換え、ファイル名をsettings.gradle.ktsとするだけで完了です。

settings.gradle.kts(書き換え後)
pluginManagement {
    repositories {
        gradlePluginPortal()
    }
}
rootProject.name = "kts-test"

build.gradleを書き換え

気合でゴニョゴニョしたので詳細は書けません(ごめんなさい)。

一応セッションの11:36辺りを参考に、「'([^']+)'"$1"」置換はやりました。
その後は気合で書き換えました。

build.gradle(書き換え前)
plugins {
    id 'org.springframework.boot' version '2.1.3.RELEASE'
    id 'org.jetbrains.kotlin.jvm' version '1.2.71'
    id 'org.jetbrains.kotlin.plugin.spring' version '1.2.71'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.wrongwrong'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.jetbrains.kotlin:kotlin-reflect'
    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

compileKotlin {
    kotlinOptions {
        freeCompilerArgs = ['-Xjsr305=strict']
        jvmTarget = '1.8'
    }
}

compileTestKotlin {
    kotlinOptions {
        freeCompilerArgs = ['-Xjsr305=strict']
        jvmTarget = '1.8'
    }
}
build.gradle.kts書き換え後
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.1.3.RELEASE"
    id("org.jetbrains.kotlin.jvm") version "1.2.71"
    id("org.jetbrains.kotlin.plugin.spring") version "1.2.71"
}

apply(plugin = "io.spring.dependency-management")

group = "com.wrongwrong"
version = "0.0.1-SNAPSHOT"
val sourceCompatibility = "1.8"

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

// KotlinCompileに関しては2通りの書き方をしているが、統一性の観点から恐らく下に合わせる方がよい
tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    }
}

val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
    freeCompilerArgs = listOf("-Xjsr305=strict")
    jvmTarget = "1.8"
}

コントローラーを追加してハロワ

追加するコントローラーは以前自分が書いた記事のものを流用します。

MyController.kt
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("my")
class MyController{
    @GetMapping
    fun myGetTest(model: Model): String{
        return "hello from spring boot"
    }
}

以下2つのdependencyが無ければコンパイルが通らない or WARNが出るので追加します。

追加するdependency
implementation(group = "org.springframework.boot", name = "spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

これを動かした上でhttp://localhost:8080/myへアクセスすることでハロワできます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む