- 投稿日:2019-03-02T18:39:11+09:00
「AndroidXでのバックキー制御」について、OnBackPressedCallbackの使い方・注意点を補足
はじめに
@androhiさんの記事「AndroidXでのバックキー制御」を読んで、これは使わない手はない!と思いプロダクトに導入し、その時にハマった点や補足を皆様に共有したいと思い記事にしました。
(@androhiさんに許可を頂いてます。ありがとうございます!)ブログの内容を掻い摘んで説明すると、
Activity#onBackPressed()
でやっていたバックキーの制御をOnBackPressedCallback
で出来るようにしてFragmentでのバックキー制御を簡単にしよう!というやつです。インストール
- 利用にはandroidxのappcompat:1.1.0とactivity:1.0.0が必要です。まずはこれらをインストールします。
app/build.gradledependencies { 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にダウングレードしています。
`NoSuchMethodException: addFontWeightStyle [class java.lang.String, int, boolean]`っていうエラー出ててなんだろーなーと調べたら 'androidx.appcompat:appcompat:1.1.0-alpha02' 使うとだめっぽい。https://t.co/omgGlZfF5R
— たる (@tarumzu) 2019年3月1日使い方
- まずActivityはandroidxのAppCompatActivityを継承させます。
MainActivity.ktimport androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { // 略SampleFragment.ktclass 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ではありますが、前述のバージョンであれば動作も安定しており、コードも非常にシンプルになるのでプロダクトで採用しても問題ないかなと思ってます。気になった方はぜひ使ってみてください!
- 投稿日:2019-03-02T18:39:11+09:00
「AndroidXでのバックキー制御」について、OnBackPressedCallbackの使い方・注意点補足
はじめに
@androhiさんの記事「AndroidXでのバックキー制御」を読んで、これは使わない手はない!と思いプロダクトに導入し、その時にハマった点や補足を皆様に共有したいと思い記事にしました。
(@androhiさんに許可を頂いてます。ありがとうございます!)ブログの内容を掻い摘んで説明すると、
Activity#onBackPressed()
でやっていたバックキーの制御をOnBackPressedCallback
で出来るようにしてFragmentでのバックキー制御を簡単にしよう!というやつです。インストール
- 利用にはandroidxのappcompat:1.1.0とactivity:1.0.0が必要です。まずはこれらをインストールします。
app/build.gradledependencies { 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にダウングレードしています。
`NoSuchMethodException: addFontWeightStyle [class java.lang.String, int, boolean]`っていうエラー出ててなんだろーなーと調べたら 'androidx.appcompat:appcompat:1.1.0-alpha02' 使うとだめっぽい。https://t.co/omgGlZfF5R
— たる (@tarumzu) 2019年3月1日使い方
- まずActivityはandroidxのAppCompatActivityを継承させます。
MainActivity.ktimport androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { // 略SampleFragment.ktclass 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ではありますが、前述のバージョンであれば動作も安定しており、コードも非常にシンプルになるのでプロダクトで採用しても問題ないかなと思ってます。気になった方はぜひ使ってみてください!
- 投稿日:2019-03-02T18:39:11+09:00
「AndroidXでのバックキー制御」について、OnBackPressedCallbackの使い方や注意点補足
はじめに
@androhiさんの記事「AndroidXでのバックキー制御」を読んで、これは使わない手はない!と思いプロダクトに導入し、その時にハマった点や補足を皆様に共有したいと思い記事にしました。
(@androhiさんに許可を頂いてます。ありがとうございます!)ブログの内容を掻い摘んで説明すると、
Activity#onBackPressed()
でやっていたバックキーの制御をAndroidXからはOnBackPressedCallback
が使えるようになったので、これを使ってFragmentでの実装を簡単にしよう!というやつです。インストール
- 利用にはandroidxのappcompat:1.1.0とactivity:1.0.0が必要です。まずはこれらをインストールします。
app/build.gradledependencies { 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にダウングレードしています。
`NoSuchMethodException: addFontWeightStyle [class java.lang.String, int, boolean]`っていうエラー出ててなんだろーなーと調べたら 'androidx.appcompat:appcompat:1.1.0-alpha02' 使うとだめっぽい。https://t.co/omgGlZfF5R
— たる (@tarumzu) 2019年3月1日使い方
- まずActivityはandroidxのAppCompatActivityを継承させます。
MainActivity.ktimport androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { // 略SampleFragment.ktclass 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ではありますが、前述のバージョンであれば動作も安定しており、コードも非常にシンプルになるのでプロダクトで採用しても問題ないかなと思ってます。気になった方はぜひ使ってみてください!
- 投稿日:2019-03-02T16:01:39+09:00
MockKの使い方
Kotlin向けMockライブラリのMockKの使い方について簡単にまとめます
MockKがどんな意図で作られているかはこちらの記事で作者が語られていますのでご参照ください。
MockKとは
- MockKはKotlin独自の言語仕様をほぼ網羅しているモックライブラリ
Coroutine
やobject
、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つずつ説明すると
val car = mockk<Car>()
- モックインスタンスを生成する
every { car.drive(Direction.NORTH) } returns Outcome.OK
- モックインスタンスにパターンを設定
- この場合は「carインスタンスのdriveメソッドが引数Direction.NORTHで呼ばれた場合はOutcome.OKを返却する」となる
car.drive(Direction.NORTH) // returns OK
- 実際に呼び出す(Outcome.OKが返却される)
verify { car.drive(Direction.NORTH) }
- メソッド呼び出しのチェック
- この場合は「carインスタンスのdriveメソッドが引数Direction.NORTHで呼び出されている」という意味
verify(exactly = 1) { car.drive(Direction.NORTH) }
というようにexactly
で呼び出されている回数を指定してチェックすることもできる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 outcome
がOutcome(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()
を利用します。
また、unmockkAll
かunmockkObject
を使用することでモックの解除もできます。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.ktpackage com.hoge.sample data class Obj(val value: Int) fun Obj.extensionFunc() = value + 5app/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にはまだまだたくさんの機能があります。
全部を紹介するとかなりのボリュームになってしまうので、自分がよく使うものを紹介させていただきました。このライブラリだけで、ここまで機能がそろっていると安心してテストがかけますね!
- 投稿日:2019-03-02T11:34:11+09:00
【Kotlin】List をコピーする / to と as の違い
List をコピーする
List などのコレクション系のクラスには、
toList
やtoSet
などの、他の型に変換する拡張関数があります。
これらは変換後の型が変換前の型と同じであっても新しいインスタンスを生成するため、
インスタンスのコピーにも使えます。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
の違い
toList
やtoSet
などの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]一方、
asIterable
やasSequence
などの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
系で生成したオブジェクトは生成元の変更に追従するため、追従する必要がないときに使うと思わぬバグを生むことになります。
しかし軽量です。違いを理解して適切に使い分けましょう。
- 投稿日:2019-03-02T11:34:11+09:00
【Kotlin】List をコピーする / to〜 と as〜 の違い
List をコピーする
List などのコレクション系のクラスには、
toList
やtoSet
などの、他の型に変換する拡張関数があります。
これらは変換後の型が変換前の型と同じであっても新しいインスタンスを生成するため、
インスタンスのコピーにも使えます。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〜
の違い
toList
やtoSet
などの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]一方、
asIterable
やasSequence
などの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〜
系で生成したオブジェクトは生成元の変更に追従するため、追従する必要がないときに使うと思わぬバグを生むことになります。
しかし軽量です。違いを理解して適切に使い分けましょう。
- 投稿日:2019-03-02T06:12:39+09:00
【SpringBoot】Kotlin DSLでビルドしてハロワするまで
やること
以下の資料を参考にしながらSpringBootをKotlin-DSLで動かします。
build.gradle.ktsに移行しよう - DroidKaigi 2019この記事で使ったリポジトリは以下です。
k163377/kts-test: Spring Boot on Kotlin DSL感想
ググれば案外情報が出てくるので、調べれば気合で色々なんとかなりました。
手順
以下の手順で進めていきます。
- Spring Initializrによる初期化
- settings.gradleの書き換え
- build.gradleの書き換え
- コントローラーを追加してハロワ
環境は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.ktimport 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が出るので追加します。追加するdependencyimplementation(group = "org.springframework.boot", name = "spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin")これを動かした上で
http://localhost:8080/my
へアクセスすることでハロワできます。