取得に失敗しました

2020年度 入部

Twitter GitHub

オキリョウ

目指せ All Kotlin Application

自己紹介

プログラミング系統を主にしています。メイン言語はKotlinです。
最近は毎日Kotlin Tシャツを着ています

将棋、刀鑑賞、ラジコン飛行機などもやってます。

何か聞きたいこと、Kotlin雑談がしたい人は入部して探すか、TwitterのDMにバシバシ送ってきてください!
https://play.google.com/store/apps/details?id=com.wsr.shopping_friend

自己紹介画像

取得に失敗しました

この人が書いた記事

プログラミング Polygon
プログラミング

KSPで自動生成コードを作る

導入プログラマーとして活動していると、コードの自動生成に憧れを抱くことがあるのではないだろうか? 自動生成をすることで作業の効率化を図ることができることはもちろん、技術マウントを取る際にも非常に役に立つ。 しかし、自動生成なんてどうやってやるのだろうか。 例えば静的言語の場合、コンパイル時に依存関係が解決されないと実行すらできない。 つまり「ソースコードを吐き出すアプリ」を作る必要がある。これは面倒である。 より簡単に自動生成をしてみたい。 そんな人にお勧めしたいのが「Kotlin Symbol Processing」、通称KSPである。 概要Kotlin Symbol Processing(以下KSP)は、軽量なコンパイラプラグイン(もしくはプリプロセッサ)の1つ。 アノテーション情報等を元にコードの生成を行うプリプロセッサ、つまりコンパイルの前に走るプログラムを作成することができる。 大体以下のような流れで処理が進む。 1. KSPで作成したプリプロセッサがアノテーションを読み取り、解析する 2. 解析情報を元に、ソースコード等を生成する 3. 生成されたコードも含めてコンパイルされる 利点実はKSP以外にもプリプロセッサ作成用ライブラリは存在する。「kapt(JavaのアノテーションプロセッサをKotlinで使えるようにしたもの)」および「kotlinc compiler plugins」である。 しかし、以下の理由からKSPの方がおススメである。 他のコンパイラプラグインと比べて、JVMのバージョンなどに依存しない Javaのプラグインを利用するkaptよりパフォーマンスがいい(Gildeの場合、コンパイル時間を25%削減)kotlinc compiler pluginを使う時よりも簡単にかけて、またAPIの変更がないように努力してくれるらしいJVMに依存しないため、他のプラットフォームでも利用可能公式がKSPを使うように推奨している 欠点いいとこ尽くしのように見えるKSPだが、当然欠点も欠点も存在する。 ソースコードの編集は不可能ソースコードを式レベルで解析することは不可能Java Annotation Processing APIで書かれたものに対する互換性はない 要は細かいことができない、Javaの遺産が使えないということである。 細かいことをしたいならkotlinc compiler plugins、Javaの遺産を使いたいならkaptを利用する必要がある。 実装方法KSPについて一通り説明したところでざっくりした使い方を紹介する。 簡単に以下のステップに分かれる。依存関係を追加SymbolProcessorを実装するSymbolProcessorProviderを実装するMETA-INFに登録するなお、KSPを用いて「コンパイル前に動くライブラリ」を作るため、別のモジュール、もしくは別のプロジェクトに切り出す必要がある。 それでは1つずつ見ていく。 依存関係を追加まずはKSPを使えるようにライブラリを追加する必要がある。 build.gradle.ktsに以下を記述する。 dependencies {    implementation("com.google.devtools.ksp:symbol-processing-api:1.8.10-1.0.9")  } SymbolProcessorを実装する。KSPではSymbolProcessorというインターフェイスが用意されている。 プリプロセッサで処理する内容はここに記述することになる。  interface SymbolProcessor {        // コード生成時に呼ばれる部分で、コードが生成されなくなるまで呼ばれ続ける      fun process(resolver: Resolver): List<KSAnnotated>            // 終了時に呼ばれるコード      fun finish() {}            // processでのエラー時に呼ばれるコード      fun onError() {}  } こちらを実装する。 実装例:プロジェクト内の関数名だけ取り出してファイルに書くコードclass SampleProcessor( private val codeGenerator: CodeGenerator, ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { val symbols = resolver.getNewFiles() symbols.forEach { file -> val output = file.declarations .filterIsInstance<KSFunctionDeclaration>() .joinToString("\n") { it.simpleName.asString() } codeGenerator.createNewFile( dependencies = Dependencies(false, file), packageName = file.packageName.asString(), fileName = file.fileName, extensionName = "", ).write(output.toByteArray()) } return emptyList() } } SymbolProcessorProviderを実装するSymbolProcessorProviderというインターフェイスが用意されているため、こちらを実装する。 こちらは2で作ったクラスのFactoryであり、インスタンスの生成方法を書けばいい。 例 class SampleProcessorProvider : SymbolProcessorProvider {      override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =         SampleProcessor(             codeGenerator = environment.codeGenerator,         )  } META-INFに登録する3で作成したSymbolProcessorProviderの実装クラスをMETA-INFに登録する。 登録場所はresources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider ここに実装クラスのルートモジュールからのパス(qualified name)を記述しておく。com.example.SampleProcessorProvider これで完成となる。あとは適応したいプロジェクトで依存関係を追記すればよい。 plugins {      id("com.google.devtools.ksp") version "1.8.10-1.0.9"      kotlin("jvm")  }    dependencies {      implementation(project(":sample-processor"))      ksp(project(":sample-processor"))  } 最後に自動生成の中では簡単・・・とはいえ少し難易度が高めではある。 それでも自動生成のコードが書けるようになることでより効率的にプログラムを書けるようになると思う。 是非一度挑戦していただきたい。 参考文献公式ページhttps://kotlinlang.org/docs/ksp-overview.html より細かく書いた記事https://scrapbox.io/wansukolll-82925906/KotlinSymbolProcessing_API%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6 分かりやすく作ったスライドhttps://speakerdeck.com/bugdog24/kspdezi-dong-sheng-cheng-kodowozuo-ru

> 内容を見る

プログラミング Polygon
プログラミング

自作AIをAndroidにのっける

動機近年、人工知能を利用したサービスが増えている。 例えばChat GPT。言語処理をしてくれるAIだが非常に精度がいい。APIも公開されており、気軽にサービスに組み込むことができる。他にも様々なAIが公開されており、人工知能を用いたサービスというのは珍しく無くなってきた。 しかし、提供されたAPIを叩いただけでは人工知能を使った感が薄く感じる。 自分でAIを用意してサービスに組み込んでみたいと思う人は少なくないはず。 自分でサーバーを用意してAPIを公開してもいいが、サーバーの維持費、通信費用などがかかってしまう。端末上で動かすことができればこの問題も解決できる。 そう思い探してみたところ、「Tensorflow Lite」に行き着いた。そこで、実際にこのライブラリを用いてAndroidアプリを作ってみたい。 Tensorflow Liteとは機械学習を行うためのライブラリとして「Tensorflow」というものがある。比較的自由に機械学習を行えるため世界中で愛されているが、モバイル端末上で実行するには少し重たすぎる。 そこでTensorflow Liteである。このライブラリを使うことでモバイル端末上(iOS、Android、Raspberry pi等)で利用できるくらいに軽量化される。 同じようにモバイル端末上で人工知能を利用するためのライブラリとして「ML Kit」というものも存在するが、これだと自分で作成したモデルを利用できない。その点ではこちらの方が(手間はかかるが)自由度が高い。 使い方下の方で小難しく書いているが使い方はものすごく簡単である。 1. tfliteモデルを用意するtfliteとはTensorflow Lite専用の型。 自分で作ってもいいし、ネットから拾ってきてもいい。ここでみんな配布してるのですぐ手に入る。 2. Androidにぶち込むimport機能を使って指定の場所にtfliteモデルを入れる。 3. 使う入れればAndroid側が勝手にモデル用のクラスを作ってくれる。だからその関数を叩けばいい 例えば以下のコード。 mnistModel.process(target.toTensorBuffer()) // これを叩くだけで識別結果が返ってくる。 このmnistModelというのが用意されたモデル用クラス(のインスタンス)。ここに生えているprocess関数に入力値を渡してやれば出力が得られる。 これだけで完成。 ものすごく簡単にできることがわかると思う。 アプリ作成実際にAndroidアプリを作ってみる。 作ったアプリは以下の通り。 数字を手書きしてボタンを押せば人工知能で識別してくれるアプリ。 返送速度もいい感じ(これはエミュレーターだが、実機でやればもう少し早くなる)。 こんな感じのアプリを作成していく 1.モデルの作成まずは人工知能のモデルづくりから始める。 Tensorflow Liteでは.tfliteという独自の拡張子のモデルを用意する必要がある。これには以下の方法が存在する。 1. Tensorflow Hubから有志が作成したモデルを取得する 2. TFLite Model Makerを使って事前学習済みのモデルを学習させて取得する 3. Tensorflowでモデルを作成してそれをtflite形式に変換する 今回は3の方式で行う。 ここからはPythonで記述する。 まずはTensorflowでモデルを作成する。  # モデル構造の設定  model = tf.keras.models.Sequential([   tf.keras.layers.Conv2D(48, kernel_size = (4, 4), activation="relu", input_shape=(28,28,1)),   tf.keras.layers.BatchNormalization(),   tf.keras.layers.Conv2D(96, kernel_size = (4, 4), activation="relu"),   tf.keras.layers.BatchNormalization(),   tf.keras.layers.Flatten(),   tf.keras.layers.Dense(200),   tf.keras.layers.BatchNormalization(),   tf.keras.layers.Dense(10),  ])    loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)  model.compile(optimizer='adam',         loss=loss_fn,         metrics=['accuracy'])  model.fit(train_input, train_label, epochs=5) このモデルにMnistのデータセットを入れて学習させたところ、識別率98.84%になった。 これを一度SavedModel形式に変換する。  model.save("mnist_model") そこからconverterを用いてtflite形式に変換する  converter = tf.lite.TFLiteConverter.from_saved_model("mnist_model")  tflite_model = converter.convert()  with open("mnist_model.tflite", 'wb') as o_:    o_.write(tflite_model) tfliteに変換することで、モバイル端末でも問題なく動作するように軽量化が行われる。 ただし、軽量化する中で精度が低下することがある。 そこで、以下のコードで精度を計測する。 # tflite形式のモデルのインスタンス化 interpreter = tf.lite.Interpreter("MnistModel.tflite") interpreter.allocate_tensors() input_details = interpreter.get_input_details() output_details = interpreter.get_output_details() # 識別結果が正しい個数 correct_num = 0 for i in range(len(test_input)): # テストデータを入力の型に合わせる input = test_input[i].reshape(input_details[0]['shape']).astype(np.float32) interpreter.set_tensor(input_details[0]['index'], input) interpreter.invoke() preds_tf_lite = interpreter.get_tensor(output_details[0]['index']) # 正答率を計算 correct_num += np.argmax(preds_tf_lite) == test_label[i] print(f"accuracy: {correct_num / len(test_input)}") 識別率は98.84%で、低下は見受けられなかった。 こうしてモデルを用意できた。 詳細なコードは以下に示す。 https://colab.research.google.com/drive/18J0DxrKAMGnMvu0C2AYZlqxREkglFfNS?usp=sharing 2. アプリ部分の作成次にアプリの部分を作成する。 様々な記事を見てみると、識別部分まで全てActivityに記述しているものが散見された。 お試しで使う分にはこれで十分ではあるが、今回は普通にアプリに組み込んでみて問題なく使えるかをみてみたい。 そこで以下のアーキテクチャに沿って開発をした。 TFLiteModelは技術的要素が多く、バックエンドよりの処理であるためRepository層に押し込んでいる。 また、Bitmap型はAndroid独特の型である。今回はビジネスロジックが特定の技術に依存するのもどうかと思い一度Imageに変換している。ただし、その場合TensorBufferやTensorImageへの変換に苦労することになる。 TFLiteではBitmap -> TensorImageへの変換関数も用意してくれているため、Bitmapに依存するように書いても十分問題ないと思われる。 後は(TFLiteModel以外の部分を)この仕様書に合わせて頑張って実装する。 3. モデルとアプリの接続まずライブラリの依存関係を記述する。 version catalogに以下を記述。  # Tensorflow Lite  tensorflow-lite = { group = "org.tensorflow", name = "tensorflow-lite", version.ref = "tensorflow-lite" }  tensorflow-lite-support = { group = "org.tensorflow", name = "tensorflow-lite-support", version.ref = "tensorflow-lite-support" }  tensorflow-lite-metadata = { group = "org.tensorflow", name = "tensorflow-lite-metadata", version.ref = "tensorflow-lite-support" } tensorflow-lite-gpuを導入することでGPUを使用することもできるようになるが、今回は省いている。 次にModelをAndroidのライブラリに入れていく。 以下の画像のように選択。 クリックすると以下のようなモーダルが出てくるため記述。 するとmlファイルが出現し、ここに格納される。 こちらのファイルを見ると、使い方が書かれている。 こちらに従ってコードを記述していく。 まずはModelのインスタンスの作成。 Dagger Hiltを使ってDIできるようにしておく。  @Module  @InstallIn(SingletonComponent::class)  class ModelProvider {    @Provides    fun providesMnistModel(      @ApplicationContext context: Context,    ): MnistModel = MnistModel.newInstance(context)  } その後、Repositoryに以下を記述する。    override suspend fun classify(         target: Image,     ): ApiResult<ClassifyResult, DomainException> = withContext(dispatcher) {         mnistModel             .process(target.toTensorBuffer())             .outputFeature0AsTensorBuffer             .floatArray             .let { buffer ->                 ClassifyResult(                     zero = buffer[0],                     one = buffer[1],                     two = buffer[2],                     three = buffer[3],                     four = buffer[4],                     five = buffer[5],                     six = buffer[6],                     seven = buffer[7],                     eight = buffer[8],                     nine = buffer[9],                 )             }.let { ApiResult.Success(it) }     }          private fun Image.toTensorBuffer() = TensorBuffer         .createFixedSize(intArrayOf(1, 28, 28), DataType.FLOAT32)         .also { buffer -> buffer.loadArray(this.pixels.toFloatArray()) } ぱっと見色々書いているように見えるが、識別自体はprocess関数を叩くだけでできており、非常に手軽である。 mnistModel.process(target.toTensorBuffer()) // これを叩くだけで識別結果が返ってくる。 ただデータの変換だけめんどくさい。実際残りのコードは全てデータの変換である。 Modelにmetadataを付与することでここら辺の記述はもう少し簡単にできるが、今回はこのまま行った。 以上で完成。結構簡単にできる。 完成したコードはこちら https://github.com/Wansuko-cmd/mnist-application 感想モデル作るのはそこまで難しくない。最悪既存やつ使えばいいアプリとの接続は面倒。入力されたものを頑張ってBufferに変換していく必要がある。(これはTensorImageを使えば解決できそう)モデルがでかい。今回のモデルサイズは37.5MB。一応Firebaseと連携させることで初期インストールしなくてもよくなるが、それでもでかい自分の作ったモデルをアプリに載せるのは結構楽しい気になった人は是非Androidを使って組んでみてほしい。公式チュートリアルに従ってやれば一日でできるようになる。

> 内容を見る

プログラミング Polygon
プログラミング

KotlinのFlow用テストライブラリ「turbine」を使ってみる

[2023/9/13更新] 記事の内容を修正、turbineのバージョンを1.0.0に修正 先日、インターン先で「turbine」を使ってテストコードを記述した。 するとレビュアーから「このライブラリなにこれすごいな」というコメント。意外に知られていないらしい。 個人的に気に入っているライブラリなので布教用に記事を書くことにした。 turbineとはKotlinのFlowから流れてくる値をテストするためのライブラリ。 Kotlin/JVMはもちろん、Kotlin/JS、Kotlin/Nativeでも動作する。 Githubはこちら 記述例class FooViewModel { val text = MutableStateFlow("") fun updateText(value: String) { text.emit(value) } } 上のプログラムのテストを記述したい。 素のKotlinで記述すると以下の通り。@Test fun updateTextのテスト() = runTest { val viewModel = FooViewModel() val text = "text" viewModel.text.collectIndexed { index, value -> when { index == 0 -> { assertThat(value).isEqualsTo("") viewModel.updateText(text) } index == 1 -> assertThat(value).isEqualsTo(text) else -> throw IndexOutOfBoundsException() } } viewModel.updateText(text) delay(1000L) // 余計な値が流れてこないかを監視 } turbineを利用すると以下の通り。fun updateTextのテスト() = runTest { val viewModel = FooViewModel() val text = "text" viewModel.text.test { assertThat(awaitItem()).isEqualsTo("") viewModel.updateText(text) assertThat(awaitItem()).isEqualsTo(text) ensureAllEventsConsumed() } } 導入方法mavenCentralにて配布されているため、依存関係を記述することで利用可能になる。 プロジェクトにGradleを採用している場合、build.gradle(.kts)に以下を追記する。 repositories { mavenCentral() } dependencies { testImplementation 'app.cash.turbine:turbine:1.0.0' } メソッド紹介(一部)test()turbineを適用するための拡張関数。 timeout等指定可能。targetFlow.test { // turbineを使ったコードをここに記述 } awaitItem()値が流れてくるのを待ち、流れてきたらその値を返す。 複数回呼び出した場合、流れてきた値順に値がかえされることになる。targetFlow.test { val first = awaitItem() // 値が流れてくるまでブロッキング。流れてきたらその値を返す val second = awaitItem() // 次の値が流れてくるまでブロッキング。 } awaitComplete()Flowが閉じたことを確認する関数。 新しく値が流れてきた場合TurbineAssertionErrorを投げる。targetFlow.test { val value = awaitItem() awaitComplete() // 新しい値が流れてきたらエラーが投げられる。 } ensureAllEventsConsumed()Flowのキューに値が残ってないことを確認する関数。 キューに値が残っていた場合TurbineAssertionErrorを投げる。 StateFlowのように、基本閉じることのないFlowだとawaitComplete()が使えないためこちらを使う。targetFlow.test { val value = awaitItem() ensureAllEventConsumed() // Flowのキューに値が残っていた場合エラーが投げられる。 } awaitError()Flowでエラーが投げられたことを確認し、返り値として返す。 値が流れてきたりFlowが終了した場合、TurbineAssertionErrorを投げる。targetFlow.test { val value = awaitItem() val throwable = awaitError() // 新しい値が流れてきたらエラーが投げられる。投げられたエラーを返り値として返す } skipItems(count: Int)引数で指定した数だけFlowに流れてきた値を無視する。 指定数分の値が流れてこなかったりエラー終了した場合、TurbineAssertionErrorを投げる。targetFlow.test { skipItems(count = 2) // 2個分の値を無視する val third = awaitItem() } 他にも複数のFlowを用いたテストを記述するための関数も存在する。 詳しくは公式のREADME.mdから。 終わりにFlowのテストは地獄になりがちだが、turbineを用いることでシンプルに記述可能である。 ぜひ自身のプロジェクトでも採用してほしい。

> 内容を見る

NoImage Polygon
プログラミング

Nullはなぜ危険なのか

こんばんは、オキリョウです Kotlin 1.7.0がついにきましたね! https://blog.jetbrains.com/kotlin/2022/05/kotlin-1-7-0-beta/ みなさん期待を膨らませていることだと思います。 ところで、このバージョンの目玉として取り上げられているものの中に、max関数及びmin関数があります。 名前を見てわかる通り、これはリストの中の最大、最小の値を取ってくる関数になります。 こんな原始的な機能すらKotlinにはなかった・・・というわけではありません。 代わりにmaxOrNull、minOrNullという関数が存在します。 では何が違うのかというと、最大値及び最小値が存在しないときの挙動です。従来 -> nullを返す今回 -> エラーを投げるとなっています。 最近ではNullは敬遠されがちです。その流れを汲み取ってこの関数を実装したのでしょう。 ものすごく良さげな機能に見えますね。 でも、実は私、あまり良い気がしていません。 とりあえずNullは危険だ!という理由でより危険なこの関数を使う人がいるかも?と思っているからです。 そこで、なぜNullが危険と謳われているのかを解説していきたいと思います。 Nullの危険性Nullの危険性に触れてみるまず、以下のコードを見てみましょう。こちらはJavaのコードになっています。public static void main(String[] args) { Integer value = 5; System.out.println(value * value); }至って普通のコードですね。出力結果は以下のようになります。25 それではこのコードを少し編集してみましょう。public static void main(String[] args) { Integer value = null; System.out.println(value * value); }value変数に入れる値をnullにしてみました。この場合どのような挙動をすると思いますか? そもそも数値型にnullなんか入らなさそうだからコンパイルすら通らなさそうです。 実はこれ、コンパイルが通るんです。 ただ、実行しようとするとエラーを吐いて止まります。Exception in thread "main" java.lang.NullPointerException Nullの正体なぜ上記のような挙動をするのでしょうか? それはnullが何かを理解すればすぐにわかります。 nullは何もないことを表す番地へのポインタです。 先程の実行例を見てみましょう。Integer value = 5;これは、5という値がとあるアドレスに格納されていて、そのアドレスの番地をvalueが保存している・・・という構造になっています。 ではnullの場合はどうでしょう?Integer value = null;これは、何も意味がないというアドレス(通常は0アドレスとからしい)の番地をvalueが保存しています。 ここから、value自身は両者とも同じようにポインタを指していることがわかります。 問題は、そのポインタの先に値がある場合とない場合があるということです。 そのせいで、実行時ではないとエラーがわからないのです。 説明がややこしくてわからん!という人は以下のように考えましょう。 Integer型とは、暗黙的にInteger or null型になっていた これらはもちろん、他の型でも当てはまります。 例外として、プリミティブ型と呼ばれるポインタを使わない型にはnullは使えません。 何が恐ろしいかここまで色々話してきましたが、結局nullの何が恐ろしいのでしょうか? まとめると以下の通りです。実行するまでエラーが出ないため、本番環境で爆発する可能性があるnullを参照した場所でエラーが起きるため、どこでnullが代入されたか(すなわち原因)を特定するのは困難nullを使わないように制御する方法がないため、人力でnullチェックをする必要がある(人力なので忘れがち)これらのことから、エンジニア界隈ではnullを恐れる人が多発する事になりました。 このことから、nullを開発した張本人も後悔の念を表明しています。 https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/ Nullの現在ここまででnullの恐ろしさを十分に理解できたのではないでしょうか? しかし、ここまで恐ろしいと言われているnullをそのまま放置する人類ではありません。 比較的最近作成された言語では十分な対策が取られています。 有名な言語としてKotlinが挙げられます。 ではどのような対策をしているのでしょうか? やっていることは単純です。null非許容型を用意したんです。 実際のコードを見てみましょうval value: Int = null // NGこのように、普通の型にはnullが入らないようにしました nullを使いたい場合は、下のように?をつけることで許容型にする必要がありますval value: Int? = null // OKまた、この?を外さない限り、Int型のメソッドは呼び出せません。 外すにはnullが変数の中に入っていないか(いわゆるnullチェック)をする必要があります。 こうすることで強制的にnullチェックをさせるようにしました。 これで Int = Int or nullみたいなことは無くなったわけです。 もちろん、nullチェックがしやすい構文も用意されており、nullとの付き合いが断然楽になりました。 まとめかつて、nullは非常に恐ろしいものでした。 最も簡単にエラーを引き起こしてしまいますし、かつ原因がわかりづらい。 しかし、現在ではコードによる強制チェックができるようになったため、このようなエラーは随分と減りました。 むしろ、確実にチェックしなくてはならない存在となったのです。 チェックしないとそもそも関数呼び出せないですからね。 そのため、現在のコード上でのnullは結構使いやすい存在になったと個人的には思っています。 かといって乱用するのもどうかとは思いますが・・・ 昔よりはnullを使うデメリットがガッツリ下がったと思ってください。 もちろん、Null非許容型が用意されていない言語の場合、nullは非常に危険です。 例えばJava, PHP等の古い言語、またモダン言語でも昔のDart等が挙げられます。 できる限り使わないようにするか、Option型の導入を検討しましょう。 それでは、お疲れ様でした! 補足冒頭で述べた「nullより例外の方が危険だ」という事について補足しておきます。 例外の何が恐ろしいのかというと、コンパイラによってチェックされない事です。 どこからでも好きに投げれて、try-catchを使ってチェックをしなくてもコードに怒られない・・・ そうですよね? ちょっと怖くないですか? 先程のnullで説明したことと同じような課題が出てきています。 このことから「近代のgoto文だ」と批判する人すらいるほどです。 私もこれには同感です。そこらでポンポンエラーを投げるくらいならnullで表現する方が安全だと思います。 大概のモダン言語上のnullであれば確実にチェックをする必要がありますので想像以上に安心して動かせます。 値が存在しなかったからnullを返すわけですし、結構自然な気もしますしね。 いや、nullじゃ表現力が足りない。型の堅牢さも足りていないから例外は必須だ!という人。良い着眼点です。 そんな人はぜひResult型について勉強してみてください。これを使えば完全に例外のない世界も実現可能です。

> 内容を見る

NoImage Polygon
プログラミング

Privateの仕様を勘違いしてました

プログラミングできる人からしたら何言ってんだという内容かもしれませんが、結構ショックを受けたので残しておきます 先日、Kotlinのコードを読んでいたら、こんな感じのコードに出会いました class Foo(private var value: String) { operator fun plus(other: Foo): Foo { return Foo(this.value + other.value) } } パッと見普通のコードなのですが、このコードのために頭を抱えさせられました というのも、別のインスタンスのprivateプロパティにアクセスするコードが書かれているからです class Foo(private var value: String) { operator fun plus(other: Foo): Foo { return Foo(this.value + other.value) } } 例えば以下のようなコードが書けてしまいます fun main() { val foo1 = Foo("Hello") val foo2 = Foo("World") println(foo1 + foo2) } 結果 Foo: { value : HelloWorld } このコードでは、foo1とfoo2は、もともとのクラスは同じですが、全く別のインスタンスです。 しかし、このように書くことで、別のインスタンスのprivateなプロパティにもアクセスできるようになっています そこで調べてみたところ、以下の記述がありました(参照先) private はそのクラス内(そのすべてのメンバーを含む)でのみ見える よく見るとインスタンスではなく、クラス内のみアクセス可能と書いていました つまり同一のクラスからできたインスタンスだとアクセス可能らしいです だから最初のコードでもコンパイル&実行できるということらしいです しかし、ここでいくつかの疑問が出てきます。そこでそこらへんも調べてみました Privateなメソッド(関数)でも同じなのか 同じように動くようです(参考先) サブクラスだとどうなるのか 動きません 仮に、サブクラスも参照可能にするprotectedを利用しても不可能でした open class Foo(protected var value: String) class Bar(private val name: String): Foo(name){ // 引数のクラスの参照はNG fun ng(other: Foo) { println(other.value) } // 自身の親の参照はOK fun ok() { println(value) } } 拡張関数だとどうなるか 無理です open class Foo(private var value: String) // NG fun Foo.print(){ println(this.value) } これはJavaにコンパイルされたときのコードを見ればわかります public static final void print(@NotNull Foo $this$print) {   // Something... } Javaには拡張関数は存在しないため、引数としてクラスを渡して、それで処理しています ですので、privateプロパティにはアクセスできないので、動きません 自身のインスタンスだけアクセスできるようにする方法はないのか 少なくとも公式からは出されていない感じがします(privateが一番厳しい制約とのこと) Kotlinの場合、DelegateとかReflectionを使えば書ける気もしないこともないですが、書いたところでコンパイルエラーになりません したがってほとんど意味を成しません ですので、コメントやアノテーションなどで、分かるようにするのが精いっぱいだと思います 他の言語だとどうか C# using System; public class Program { public static void Main() { Foo foo1 = new Foo("Hello"); Foo foo2 = new Foo("World"); Console.WriteLine(foo1.getValue(foo2)); } } public class Foo { String value; public Foo(String value) { this.value = value; } public String getValue(Foo other) { return other.value; } } 結果 World 動いています PHP <?php $foo1 = new Foo("Hello"); $foo2 = new Foo("World"); print $foo1->getValue($foo2); class Foo {     private string $value;          function __construct(string $value)     {         $this->value = $value;     }          function getValue(Foo $other): string     {         return $other->value;     } } 結果 World 動いています ですので、Java、C#、PHPで動くため、おそらくほぼすべてのプログラミング言語はこの仕様だと考えられます まとめ 最初見たときは本当に驚きましたが、冷静になって考えるとこちらの方が理にかなっていますね 同一インスタンスじゃないとアクセスできない!としないといけない場面てほとんどないでしょうし むしろこちらの方が、クラスの役割をきっちりかけていい感じがします 今までずっと勘違いしてきたことを今更知って本当に恥ずかしいです・・・

> 内容を見る

プログラミング Polygon
プログラミング

Jetpack Composeで、Mutable系統をStateに登録したらあかん

タイトルで書いてあることを悟るまでに数日潰したので、怒鳴り散らしながらメモとして残します。 Jetpack ComposeとはAndroid開発において新たな時代の到来を感じさせるUI Toolkitです。 Androidは十数年前に登場して以降、様々なアプリが開発されてきています。 その影響もあり、Android開発を行うときは十数年前のクソみたいな古い風習が残っています。 例えばUIを作成するときです。 Androidでは命令型UIを採用しており、XMLと呼ばれる言語で書いていく必要がありました。 以下のような奴です<?xml version="1.0" encoding="utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/LinearLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:background="@android:color/white" android:elevation="4dp" android:orientation="horizontal"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/title" android:layout_weight="1" android:gravity="center" android:layout_gravity="center_vertical" android:textSize="30sp" android:layout_height="wrap_content" android:layout_width="0dp" android:clickable="true" android:text="@string/title" android:focusable="true" /> <androidx.appcompat.widget.AppCompatImageButton android:id="@+id/delete_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_delete_title" android:contentDescription="@string/delete" /> </androidx.appcompat.widget.LinearLayoutCompat> こんなコードから以下のようなUIが出力されます こんなのコードだけ見ても何を表しているのか、慣れていない人にとってはさっぱりわかりません。慣れててもよくわかりません。 これだけならいいのですが、ここからデータを表示するとなると、KotlinやJavaでコードを書く必要があります。 その書き方がかなりめんどくさいわけです。例えば、以下のような感じになります。 val binding = ChecklistTitleBinding.inflate(inflater, parent, false) val title: TextView = binding.title //要素の文字の部分の設定 title.apply { text = "Hello World" textSize = getTextSize(context).toFloat() //タイトル名をクリックした際の処理 setOnClickListener { clickTitleOnListener(title.text.toString()) } } この通り、部品一つずつ指定して書いていく必要があります。 これで、部品の名前とかはXMLで書いているのでそっちを見ないといけないし、指定するのを忘れていたらバグにつながります。 そこでJetpack Composeの出番です。 これは最近はやっている宣言的UIを採用しており、パッと見たらわかる感じになっています。 例えば、以下のように書くと、先ほど紹介したコードと同じことができます。 @Composable fun LazyCard(text: String = "Hello World"){ Card( modifier = Modifier .padding(10.dp) .fillMaxWidth() ) { Row( modifier = Modifier.padding(12.dp) ) { Text( text = text, modifier = Modifier.weight(1f) ) Icon( painter = painterResource(R.drawable.ic_baseline_delete_24), contentDescription = null ) } } } 以下のような感じですね これだと、コードを見ただけで、先ほどよりは断然分かりやすいと思います。 どのような見た目をしているかだったり、どこにデータが流れていくかであったりが分かりやすいので非常に使いやすいです。 このように、Jetpack Composeは、Androidの新時代を感じさせるUI Toolkitになっています。 本題やっと本題です。 Kotlinには複数のデータを扱うために、標準ライブラリの中にいくつかの型(class)が存在します 例えば配列を扱うためのList型、複数のPair型(keyとvalueを持った型)を扱うためのMap型などがそれにあたります。 これらの型は、一度値を作成したら値を追加、削除することができません しかし、これらを継承した型である、MutableList型、MutableMap型等、Mutable系統であれば可能ですval list = listOf("は", "ひ", "ふ") //コンパイルエラー list.add("へ") val mutableList = mutableListOf("は", "ひ", "ふ") //これはOK mutableList.add("へ") 結構扱いやすく、私もよく利用している型です。 そのため、Jetpack Composeにおいても何気なく使ったことが運の尽きでした。 ある日、こんな感じの、+ボタンを押したらカードが追加されていくアプリを作成しました。 ソースコードは以下の通りです。@Composable fun List(){ //副作用 var list by remember { mutableStateOf((0..3).toMutableList()) } //UI部分 LazyColumn(Modifier.fillMaxSize()){ item{ //カード表示部分 for(i in list){ LazyCard(text = i.toString()) } } item { //ボタン AddButton( //ボタンを押したときの処理 onClick = { val tempList = list tempList.add(4) list = tempList } ) } } } (副作用ってなんやねんて話ですが、話し出すと記事が一つできるような内容ですので省きますが、簡単に言うと、「関数外のところに影響を及ぼす部分」のことです。) このremember{}の部分が何をしているかというと、画面に表示するためのMutableListを保存しています。 rememberのところでState型を保有しておき、それを返してくれます。 これを変数に入れておき、値が変更されると、UIを更新してくれる優れものです。 今回の場合ですと、Listの中身が追加されれば、表示されるリストの数も追加されて以下のようになるはずです。 そして、画面内の+ボタンを押すと、Listの中身が追加されるようになっています。//ボタン AddButton( //ボタンを押したときの処理 onClick = { //別の変数に代入 val tempList = list //変数を追加   tempList.add(4) //listに反映 list = tempList } )つまり、このボタンを押せば増えてくれるはずです。 しかし、実際には増えてくれません。 この挙動のせいで数日間頭を抱える事態に陥ってしまいました 原因結論から書くと、rememberで渡された値が参照渡しになっていることでした。 私の意図した動きは以下の通りです//副作用の部分 var list by remember { mutableStateOf((0..3).toMutableList()) } AddButton( onClick = { //別の変数に、実際の値のコピーを代入 val tempList = list //tempListのリストに、変数を追加   tempList.add(4) //本体のlistに反映(ここで差分を検知してUIの再描画が走る) list = tempList } ) しかし、実際にはこう動いていました。//副作用の部分 var list by remember { mutableStateOf((0..3).toMutableList()) } AddButton( onClick = { //別の変数に、listの参照先を代入 val tempList = list //tempListのリストに、変数を追加(ここで、listの方にも変数が追加される。この追加方法ではUIの再描画が走らない)   tempList.add(4) //本体のlistとtempListの両方に値が追加されたせいで、差分が一切ないため、UIの再描画が走らない list = tempList } ) いろいろと調べてると、どうやらKotlin(というよりJava)では、クラス型は参照渡しを採用しているらしいです。 http://syrinx.q.t.u-tokyo.ac.jp/tori/java/basic/entity_ref.html Mutable系統もクラス型なので参照渡しということですね。 解決策値を新しく作って、ぶち込めば解決です。//副作用の部分 var list by remember { mutableStateOf((0..3).toList()) } AddButton( onClick = { list = list + listOf(list.size) } ) listに、新しくList型を作り直して、それをぶち込んでいます。 ここで重要なのが、Mutable型の特徴であるadd()関数では差分を検知してくれないということです。 ですので、Mutable型を使うことに対するメリットがないばかりか、意図せず使ってしまい、バグを誘発する危険性があります。 だからこそ、Mutable型をStateの部分(rememberのところ)では避けて、普通の型を使うべきでしょう。 以上 しかし、こんな記事俺以外に誰が見るんだろうか・・・

> 内容を見る

プログラミング Polygon
プログラミング

Ktorを知る ~Pipeline編~

こんにちは、オキリョウと申します! 前回、Ktorの特徴について解説させていただきました。(https://oucrc.net/articles/ia_sv-bbmz) そこで今回はKtorで重要な概念であるPipelineについて解説していきたいと思います。 PipelineとはPipelineとは、Ktorにおいて処理を順序良く処理していくためのものです。 ライフサイクルを定義するためのもの、みたいな感じに考えていただけてたらと思います。 前回、Ktorには以下の特徴があるといいました。拡張性が高く、柔軟非同期処理に強いこれらはPipelineを導入することで実現しています。 Ktor内では、継承可能なクラスとして定義されており、そこらじゅうで使われています。Ktor内のクラスの継承元をたどっていくと一枚嚙んでいた・・・ということも珍しくありません。 それではより詳しく見ていきましょう。 Pipelineを図で表すと以下のようになります。 この画像の場合、オレンジ色の向きへと処理が流れていきます。 つまりフェーズ(Phase)1->フェーズ2->フェーズ3->フェーズ4の順番に処理が流れていくということです。 各フェーズ同士は依存していません。しかし、共有する必要のある値ももちろん存在します。 それらはPipelineが所有しているPipelineContextに保存しておき、そこから値を取り出す仕組みになっています。 また、フェーズは好きなところに付け足すことも可能です。 このように、左から右へと、それぞれ独立したフェーズを処理していく流れがPipelineです。 Pipelineの使用例では、Pipelineがどのように使われているのか見てみましょう。 例えば、サーバーサイドアプリケーションの中でも必須である、「リクエストを受けとってリスポンスを返す」という部分はPipelineで実装されています。(ApplicationCallPipelineという名前のクラスです) このオレンジ色の矢印がApplicationCallPipelineです。 こんな感じに差し込まれています。 リクエストを受けとって、そこから各フェーズを実行してリスポンスを作成、返すという感じです。 ではより具体的に見ていきましょう。 このような感じになっています。 左から順番にSetup -> Monitoring -> Features -> Call -> Fallbackの順番に処理が流れています。これら青い部分がフェーズですね。 これらのフェーズの役割は以下の通りです Setup: callやattributesを用意する Monitoring: callを追跡するための部分で、ログやコードの評価を行う、エラーハンドリングなどに使われる Feature: 大体のFeatureが入るところ Call: callを完全に作りきるために利用するところ Fallback: ハンドリングされていないcallを何らかの形で処理する部分 補足call:ApplicationCallの事。リスポンスであったりattributesであったりを管理している attributes:DIコンテナ(多分、詳しく知りたい人は公式をチェック) Feature:Ktorの追加機能の事。後述 それぞれのフェーズを順に通していく中で徐々にリスポンスを作成しています。 この流れがPipelineです。 ちなみにRouting等もPipelineを使用しています。 ではPipelineについてある程度説明したところで拡張性が高く、柔軟非同期処理に強いを実現している方法について説明していきたいと思います。 拡張性が高く柔軟Pipelineはそれぞれのフェーズを定められた順に実行していくことは先ほどまで書いた通りです。 また、これらのフェーズは互いに依存しておらず、好きなところに差し込むことができるということも説明したと思います。 必要なフェーズを差し込んだり、要らないフェーズを抜くことができるということは、必要な処理だけを記述することができるということです。 それに加えて、実はフェーズの間に処理を入れることも可能です。(intercept) 先ほどちらっと登場したFeatureというのは、このような処理を練りこんだライブラリといえます。 そのようにすることで、本来複雑になるはずのライブラリの作成、導入が非常にシンプルにできるわけです。 せっかくですので、リスポンスのヘッダーに現在時刻を付ける、というFeatureを定義、導入したコードを掲載します。 まずは現在時刻を付けるFeatureの定義からpackage com.example import io.ktor.application.* import io.ktor.response.* import io.ktor.util.* import io.ktor.util.pipeline.* import java.time.LocalDateTime class SendTimeHeader(configuration: Configuration) { private val name = configuration.headerName //設定 //この場合、ヘッダーのkeyの部分を設定できるようにする class Configuration{ var headerName: String = "Send-Time" } //差し込まれる処理 private fun intercept(context: PipelineContext<Unit, ApplicationCall>){ //設定された値と、現在時刻をheaderにセット context.call.response.header(name, LocalDateTime.now()) } //アプリケーション実行時に行われる処理 companion object Feature : ApplicationFeature<ApplicationCallPipeline, Configuration, SendTimeHeader>{ //Attributeを設定 override val key: AttributeKey<SendTimeHeader> = AttributeKey("SendTimeHeader") //install時の処理 override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): SendTimeHeader { val configuration = Configuration().apply(configure) //インスタンスの作成 val feature = SendTimeHeader(configuration) //ApplicationCallPipelineのCallというフェーズに以下の処理を入れる pipeline.intercept(ApplicationCallPipeline.Call){ //先ほど定義した、interceptの処理 feature.intercept(this) } return feature } } } こんな感じです。 非常に見づらくなってしまい申し訳ないですが、雰囲気は理解できたかと思います。 実装の流れはそのうち別の記事で説明すると思います。 ちなみに今回の場合は、ApplicationCallPipeline↓ のCallのところにこちらの処理が入ります。 それでは利用するときのコードも掲載します package com.example import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.* import io.ktor.util.* import io.ktor.util.pipeline.* import kotlinx.coroutines.launch fun main(args: Array<String>) = io.ktor.server.netty.EngineMain.main(args) fun Application.main(){ //先ほどのFeatureの導入 install(SendTimeHeader){ headerName = "Time" } routing { //ホームルートにアクセスしたときに「Hello World」を返す get("/"){ call.respondText("Hello World") } } } これだけですね。 installのところでFeatureを指定(必要ならば設定も行う)するだけで、すべてのルートにて、現在時刻が追加で刻まれたヘッダーが返却されます。 一見複雑に見えるかもしれませんが、普段は利用するだけ、つまり下側だけですので非常にシンプルです。 上側の方も、やっていることはそこまで複雑ではなく、他のフレームワークで同じことをすることを考えたらはるかに簡単だと思います。 他にも認証機能やログを取る機能もこんな感じで簡単に導入できます。 このように、簡単にいろいろなライブラリを導入したり、作成したりすることができます。 ですので、自分が好きなライブラリで固めることも可能ですし、かなり規模の大きいアプリケーションを作成することも全然可能です。 これもPipelineだからできることです。 非同期処理に強い前回、KtorではCoroutineというものを使い倒しており、そのために非同期処理に強いという話を書きました。 Pipelineもこの流れを強く受けています。というのも、PipelineContextはCoroutine Scopeを継承しており、Coroutine Scopeとして扱うことができるためです。 例えばrouting { //ホームルートにアクセスしたときに「Hello World」を返す get("/"){ launch{ TODO("Coroutineを起動") } } } という感じで、さらっとCoroutine builderを呼び出すことができます。 get()関数は引数としてPipelineContextを取るからですね。 このように、わざわざCoroutine Scopeを定義する必要もないため、気軽に非同期処理を書くことができます。 また、非同期処理が強い理由としてProceed関数およびProceedWith関数の存在もあります。 ややこしい説明になりますが、この関数を実行したインターセプター(フェーズの間に入れる処理群の事)の処理は後回しにされます。 なんの役に立つのかと思うかもしれませんが、意外と使えます。 例えばかなり重たい処理があったとします。その時はPipelineと違うCoroutineを立ち上げて、その後Proceed関数を実行します。 本来だとCoroutineを立ち上げた場合は、そのCoroutineの処理が終わるまで待機しないといけないため、Coroutineの処理が長ければ長いほどレスポンスが遅くなります。 しかし、後回しにすることで、かなり重たい処理とほかの処理を並列に実行することができます。 重たい処理の完了を待つことなく別の処理を進めることができるため、早く処理を終わらすことができるというわけです。 このようにPipelineを用いることで、非同期処理を簡単に、しかし効果的に書き上げることができるのです。 他にもPipelineではCoroutineを生かして処理の効率化を図っていますが、ここでは省略しています。 まとめいかがだったでしょうか?ちょっと説明が難しくなってしまい、申し訳ございません。 ただ、このフレームワークはかなり面白いので、実際に組んでみる価値は大いにあります。 また、公式がより分かりやすく説明してくれています(英語ですが) ぜひそちらの方にも目を通してみてください! あと、ここまでの説明は公式の解説と、コードを読んでみた結果から書いています。 何かしら間違っていたら申し訳ございません。

> 内容を見る

プログラミング Polygon
プログラミング

Ktorを知る ~基礎編~

こんにちは、オキリョウと申します! 先日、Ktor等Webアプリケーションフレームワークを使ってみたのですが、結構気に入りました。そこで、せっかくなのでKtorについて記事にすることで、より深く理解していきたいと思い、書くことにしました。 KtorとはKtorはKotlinで書かれた、かなり新しいWebアプリケーションフレームワークです(2018年にバージョン1.0を発表)。オープンソースとしてJetbrains社が中心となって開発しています。 ↑ Ktorのロゴです。少し怖い 公式によるとKtorの目標は the goal of Ktor is to provide an end-to-end multiplatform application framework for connected applications.  ということらしいです。つまりサーバーからクライアント、windowsからiosにいたる全部の環境で動くフレームワークを目指しているようです。欲張りですね。 コードは以下のようになっています。これは Hello World を出力するプログラムですね package com.example import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.* fun main(args: Array<String>) = io.ktor.server.netty.EngineMain.main(args) fun Application.main(){ routing { get("/"){ call.respondText("Hello World!") } } } こんな感じの雰囲気です。 Ktorの主な特徴は以下の通りです。とにかく軽い拡張性が高く、柔軟非同期処理に強いどこでも動く 一つずつ見ていきましょう。 とにかく軽いKtorはとにかく軽いです。というのも、何も実装されていないところから開発できるからです。 Webフレームワークといえば、SpringやDjango, Laravelなどがあると思います。これらのフレームワークは、認証機能やシリアライザー、ORMなどの必要な機能がデフォルトで搭載されています。逆にKtorは何も搭載されていません。その代わりに、必要なものだけ書いて使用することができます。そのようにすることで、かなり軽いフレームワークとなっています。 拡張性が高く、柔軟先ほど、必要なものだけを使用することができると書きました。逆に言えば、Ktorは必要なものをどんどん取り込む機能が充実しているとも言えます。例えば、テンプレートエンジン「FreeMarker」を使いたい場合、以下のように書けばOKです。 install(FreeMarker) { templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates") outputFormat = HTMLOutputFormat.INSTANCE } もちろんですが、他のテンプレートエンジンを選ぶこともできます。 ちなみに自分でこのようなプラグインを書くことも全然可能です。 このように、Ktorでは好きなように拡張できますし、使いたいものを使うことができます。 非同期処理に強いKtorは非同期処理に強いです。どれほど強いかというと、公式が出しているチュートリアルの中に、チャットアプリを作成するというものがあるくらいです。 というのも、Ktorは、Kotlinのライブラリの一つである「Coroutine」をものすごく使っています。 ここではCoroutineの詳しい説明は省きます(それだけで記事ができるくらいややこしい)が、このCoroutineを使うことで非同期処理に相当強くなっています。 どこでも動くKtorの目標として、マルチプラットフォーム上で動くというものがありました。 現在も開発中ということですが、公式によると、今のところサーバーサイドはJVM、クライエントサイドはJVM、JavaScript、IOS、Androidで動くようです。ですので、ほとんどの環境で動きます。 Kotlin自体がどこでも動くので、Kotlin一つでWeb系を制覇できる日が来るかもしれませんね。 ちなみに公式では書いていませんが、Android上でもサーバーを立てることが可能です。 https://qiita.com/oxsoft/items/aec71882b1b21930c953 何に使うのかといわれるとアレですが・・・ まとめKtorについていろいろ書いてきました。このように魅力があふれかえるフレームワークとなっています。 唯一欠点があるとすればドキュメントがそこまで多くないことでしょうか。できてから数年しかたってないので仕方がない部分ではあります。 といっても公式を見ておけば大体何とでもなります。 実際書くこと自体はそこまで難しくないので一度挑戦してみるのもありだと思います。

> 内容を見る

プログラミング Polygon
プログラミング

2. 一からアンドロイド開発をして思った、知っておくべきこと~ViewBinding編~

こんにちは、オキリョウと申します! このシリーズでは私が初めてAndroidアプリ(下のURLのやつ)を作成した時に「最初から知っておきたかったな・・・」と感じたことを書いていきたいと思います。   是非見ていってください↓↓↓ https://play.google.com/store/apps/details?id=com.wsr.shopping_friend 今回紹介するのは「ViewBinding」です。  どういう内容?Androidのコードを書いていると、Viewを操作したり、Event発生時の処理を書いたりしたくなる時が多々あります。 例えば、画面上にあるボタンを押したらメッセージが出てくる、といった実装をするときです。そのような時、どういう風に書くのかというのが今回の議題です。  1.findViewByIdまず、古い記事などでよくみられるのがfindViewByIdです。val button = findViewById<Button>(R.id.button) button.setOnClickListener {     //処理を書く }このように書くことで自分の指定したid(この場合だとbutton)のボタンが押されたときの処理を書くことが出来るようになります。 ただ、このfindViewByIdはいくつか問題を抱えていました。コードが複雑になったり、返り値がnullableだったり… 幸いにも私はfindViewByIdを利用するのはあまりよろしくないという記事を発見したので無事回避することが出来ました。  2.Kotlin Android Extensions次に考えられるのがKotlin Android Extensionsを使った方法です。 こちらはGradleファイルに記述を加える事で使えるようになるものです。これを使うことで、先ほどのコードは以下のように書くことが出来ます。button.setOnClickListener {    //処理を書く }findViewByIdというのが無くなってますね。 これは、プログラム側が勝手にfindViewByIdをしてくれ、id名と同じ名前の変数に代入してくれているからです。 これによりfindViewByIdよりも簡潔に書けるようになりました。ついでにfindViewByIdが持っていた「型安全ではない」という問題も解決してくれています。 これだけ聞くとかなり優秀そうです。実際にKotlin Android Extensionsを使っていたり、称賛している記事も多くありました。 そこで私のアプリにも導入して、コーディングしていました。   しかし、この2020年11月24日にサポートが終了してしまいました。 というのも、Kotlin Android Extensions自体もまた問題をいくつか抱えていたからです。 まずKotlinでしか動かないことです。そりゃあ、KotlinのExtensionsなんだから当たり前の話です。しかし、古くからあったアプリとかがJavaで組まれていることを考えると無視できない問題であることが分かります。 次にnullableであることです。つまり、findViewByIdが抱えていた問題が解決できていないということです。 そして、コードが汚くなるということです。 Kotlin Android ExtensionsではXMLで定義されたidと全く同じ名前の変数にいろいろ準備してくれます。 しかし、そもそもXMLはスネークケース(kotlin_is_best)、Kotlinではキャメルケース(kotlinIsBest)と命名規則が違います。したがってKotlinのコードに本来あるべきではないスネークケースが紛れ込むことになります。 これらの理由からサポートが終了してしまったとのことです。  3.ViewBindingそしてこれらの問題をほぼ全部解決するのがViewBindingとなります。 このViewBindingがどういうものなのかを書くと長くなってしまうので省略しますが、導入することにデメリットはないという人もいるくらいのものです。必ず導入してあげましょう。 参考URL: https://developer.android.com/topic/libraries/view-binding?hl=ja   ちなみに私のアプリは現在懸命にKotlin Android Extensionsから移行している最中です…皆さんも気を付けましょう。

> 内容を見る

プログラミング Polygon
プログラミング

1. 一からアンドロイド開発をして思った、知っておくべきこと~Fragment編~

こんにちは、オキリョウと申します! このシリーズでは私が初めてAndroidアプリ(下のURLのやつ)を作成した時に「最初から知っておきたかったな・・・」と感じたことを書いていきたいと思います。   是非見ていってください↓↓↓ https://play.google.com/store/apps/details?id=com.wsr.shopping_friend 今回紹介するのは「Fragment」です。  ActivityとはFragmentが何なのかを紹介する前に、まずActivityについて書きたいと思います。 このActivityというのは、Androidのアプリにてメインで動く部分です おそらくAndroidの入門記事を読むと、MainActivityというファイルにコードを書いていると思います。このMainActivityというのがActivityの一種です。   まだまだひよっこだった私は様々な入門記事を読み漁りました。そういった入門記事のほとんどはそこまで複雑なことをしないものですからMainActivityにコードを書いています。 それを見た私は「Activityに全部書くんだな」と勝手に思い、全部書き込んでしまいました。 全部書いてしまうことの欠点実は全部の処理をActivityに書くのはそこまでいいわけではありません。Activityに書いてしまうと使いまわしが出来なくなるからです。下の画像を見てみてください。 こちらは実際に作ったAndroidアプリとなります。 左の画面にあるボタンをクリックすると右の画面に遷移するのですが、共通部分がありますね。写真には撮りませんでしたが、一番下の空白、そこも広告が入るので共通部分となります。 これらを全てActivityで書いてしまうと、画面遷移前と画面遷移後、どちらのプログラムコードにもこれらの記述を書く必要があり、非常に面倒です。 面倒なだけならいいですが、実装できなくなる例もあります。例えばBottom Navigation Barを実装するときにActivityに全部書くと、Googleが用意してくれているやつが使えなくなるので血の涙を流すことになります。 Fragment君 Activityに全部書くのは非効率で、使えるものも制限されてしまうことが分かったと思います。 ではどのようにすればいいのでしょうか? ここで冒頭のFragmentが登場します。Fragmentとはその名の通り、パーツを指します。Acitivityというメイン画面に使えるパーツというわけですね。 このFragment、何と簡単に差し替えることが出来ます。 AcitivityにFragmentを置く場所を置いておけば、あとは変え放題です。 つまり、共通部分はActivityに、違う部分はFragmentにおいてやれば再利用が出来ます。 もっと言えば、Fragmentは差し替えるものということで設計されているので、簡単にわかりやすく変えることが出来ます。 詳しくはNavigationで検索しましょう。一応参考URLも貼っておきます https://qiita.com/tktktks10/items/7df56b4795d907a4cd31 まとめFragmentを実装すること自体はたいして難しいものではありません。調べればある程度の資料は出てくると思います。ですので、しっかり調べて、どのような時にFragmentを使うのかなど知っておくのがベストだと思います。 ちなみに私はFragmentについて知った後、アプリをごっそり書き換えました…皆さんも気を付けましょう。

> 内容を見る

プログラミング Polygon
プログラミング

後編:Kotlinの批評をしてみた~不満編~

こんにちは、オキリョウと申します! 前回の記事ではKotlinを学習するメリットについて書かせていただきました。 しかし、メリットばっかり書くのはあまりよろしくないように感じます。 そこで今回は、私がKotlinを学ぶ上で感じた不満等について書いていきたいと思います。 *前回の記事も合わせて読まないと偏った考え方を持ってしまう可能性があります。気をつけてください  1.   プログラムを初めて学ぶ人には敷居が高いそもそもKotlinはJavaの後釜となるべく作られた言語です。なので、Kotlinを勉強する人の大半はJavaも勉強したことある人です。 それに伴ってKotlinの入門記事はjava等の他の言語を勉強していることが前提のものが多いです。 実際に前回紹介したKotlin Koansも初めてプログラミングを触る人にとってはかなり難易度の高いものとなっています。 実際に私も最初にKoansをやって挫折しかけました。 もしKotlinから学びたいという人はAndroidの入門記事などで勉強したほうがいいかもしれません。また、Kotlinを知っている人に聞いて回るのも手だと思います。  2.   新しすぎるKotlinは2011年にできた言語です。しかし、実際に注目を浴びたのは、googleがKotlinをandroid用の言語として正式に採用した2017年です。ですので、Android開発に関しては実質3年分の情報しかありません。 そのため、Androidのアプリを組む際には、Javaで書かれたコードを参照することもよくあります。 Androidはましな方です。 例えばデスクトップ用アプリを作る際に使う「Compose for Desktop」は発表されてからまだ半年すら経っていません。完全に情報不足です。 従って、実際に組むときはかなり試行錯誤する必要があります。  3.   気軽にスクリプトを書くことはできないKotlinは静的言語ですので、毎回コンパイルが必要になります。しっかりしたアプリを作るうえでは非常に心強いですが、簡単なスクリプトを書こうとしたときには逆に障害になります。 例えばパソコンの自動化をしたい!となったときです。Pythonだとファイルにコードを数十行も書けば実現可能です。コンパイルとかも必要なく、長くても1時間あれば完成するでしょう。 しかしKotlinでそれをしようとするとそれこそ「Compose for Desktop」を使ってアプリを作ることになると思います。勉強時間等を兼ねて大体1週間くらいかかるでしょう。もちろんクオリティはいいものになるとは思いますが・・・ このように、Kotlinで何かを作るとなると腰を据える必要があります。  まとめKotlinはその性質上、初心者には厳しい言語と言えます。また、比較的新しい言語でまだまだ発展途上です。そのため情報不足なところが多いです。気軽に何でもKotlinで・・・とはいきづらいところがあります。 しかし、それらに目をつぶってもいいような魅力がKotlinにはあります。勉強する価値も十分にあります。皆さんもぜひ、Kotlinを勉強してみてください!そしてもっと情報を増やしてください。

> 内容を見る

プログラミング Polygon
プログラミング

前編:Kotlinの批評をしてみた~メリット編~

こんにちは、オキリョウと申します! 私はプログラミング言語のひとつである「Kotlin」に非常に思い入れがあります。 最初に作ったアプリはKotlinで書きましたし、Qiitaに書いた記事も全部Kotlinが絡んでいます。 そこで、そんなKotlinを2回に分けて布教批評していきたいと思います。何番煎じだという感じですが、自分なりの目線で書くので読んでいってください。   今回はKotlinを勉強するメリットについて書いていきたいと思います。  1.   スマートに書けるKotlinの文法の特徴として簡潔に、分かりやすく書けるというものがあります。以下のコードを見てください。fun main(){    for(i in 1..20){        when{            i % 15 == 0 -> println("FizzBuzz")            i % 3 == 0 -> println("Fizz")            i % 5 == 0 -> println("Buzz")            else -> println(i)        }    } } よくあるFizzBuzz問題の回答をKotlinで書いてみました。どうですか?簡潔で美しく感じませんか?   このコードも見てくださいfun match(data1: String, data2:String?): String{     data2?.let{ it ->         return if(data1 == it){             "Match"         }else{             "Not Match"         }     }     return "data2 is null" }こちらは2つのデータが同じかどうかを確認する関数ですね。また、片方の引数はnullableです。 そもそもKotlinはnull安全を採用しているので、いちいちnullかどうかで処理を書く苦労を減らすことが出来ます。また、nullチェックをするときでも2行目のように書いてやればスマートに書けます。 Return文もKotlinならではの書き方です。Kotlinのifはそれ自体が式なので、このように書いてやるだけできっちり処理してくれます。 こんな感じでKotlinを使うことでかなりスマートに書くことが出来ます。他にもスマートに書くための様々な文法があります。ぜひ一度調べてみてください。 ちなみに私はdata classが一番気に入っています。  2.   なんでも書けるKotlinなら何でも書けます。マジで何でも書けます。 KotlinはAndroidアプリしか書けないと考えている人もいるでしょう。しかし、そうではありません。 バックエンドからフロントエンド、デスクトップ用のアプリ、さらにはIOSアプリまで書くことが出来ます。 一つ勉強すれば全部書くことが出来るというのは結構魅了的なことだと思いませんか?  3.   響きがいいこう、Kotlinと言われるとかわいらしい感じがしますよね?それだけで勉強していく価値は十分にあります。 ちなみにKotlinというのはフィンランド語で「やかん」の意味ですので、小鳥とは全く関係ありません。が、公式が用意しているKotlin例題集「Kotlin Koans」は、日本語の「公案」から来ているそうです。親しみがわきますね! ↑昔のロゴ  まとめKotlinを使うことで、スマートなプログラムを簡単に書くことが出来ます。また、Kotlinであればできないことはほぼありません。 ちなみにですが、聞いた話によるとAndroid開発者は人材不足らしいです。つまりKotlinを勉強しておけば就職にも優位に働くかもしれません。 皆さんもKotlinを勉強して素晴らしいプログラミングライフを過ごしましょう!

> 内容を見る