ブログに実行可能なKotlinコードを貼り付ける

ブログなどのサイト上で実行可能なKotlinコードを表示するためのスクリプト(Kotlin Playground)がJetBrainsから公開され、 Try Kotlin や Kotlinのリファレンスにあるような機能を自分のサイトでも使うことが可能になりました。

f:id:scache:20180428035614p:plain:w300

all - Kotlin Programming Language

使い方

scriptの記述

codeタグ内にコードを記述する場合は以下のコードをヘッダーなどに記述します。

<script src="https://unpkg.com/kotlin-playground@1" data-selector="code"></script>

上の記述ではcodeタグすべてにKotlinPlaygroundが適用されてしまいます。
はてなブログMarkdownで使う場合には以下のようにKotlinコードのみで適用されるようにするのが良いでしょう。
以後の説明ではこちらを使っていると仮定して進めていきます。

<script src="https://unpkg.com/kotlin-playground@1"></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    KotlinPlayground('.lang-kotlin');
  });
</script>

以上で準備は終了になります。

コードの記述

はてなブログMarkdownの場合は、以下のようにコードを記述します f:id:scache:20180428035057p:plain:w300

実際の表示

以下にKotlin Playgroundを使ったコードを表示しています。

fun main(args : Array<String>) {
    println("Hello Kotliner!")
    println("Click this green button at the top right!")
}

最後に

  • はてなブログなど、自分で自由にいじるのが難しい環境では少し扱いずらい部分がある
  • KotlinPlaygroundが少し重い?

さらに詳しい情報は以下を参照してください

Embedding Kotlin Playground | Kotlin Blog

github.com

SequenceとList(Array)

TL;DR

Sequence1つの値に対する操作が全て行われた後、必要なら次のが処理される。
Listすべての値に対して1つの操作が行われた後、必要なら次の操作が行われる

Kotlinでのデータの集合

Kotlinにはデータの集合を操作するためのクラスとして、List, Array, Sequenceがあります。
ListやArrayは他の多くに言語にもあるため想像がつくかと思います。では、Sequenceとは何なのでしょうか?
Sequenceを理解するためにList, Arrayとの違いをみていきましょう。

List vs Array

ListArrayはそれぞれの要素に対して操作を行うという観点ではほとんど同じと言えます。大きな違いとしてはArrayJavaの配列型と同等の扱いになります。Javaとの互換性を保つためにArrayがあると推測できます。

以降の文ではArrayは無視してSequenceListについて書いていきます。

Sequence vs List

実際の処理を見ると違いがわかりやすいので、
(1, 2, 3, 4)の値を持ったSequenceとListに対して以下の処理を行う場合の動作をみていきます

  1. それぞれの要素を二乗する : map{v -> v * v}
  2. それぞれの要素に1を足す : map{v -> v + 1}
  3. 値が5の要素を除く : filter{v != 5}
  4. 残った要素から最初に2つを取り出す: take(2)
  5. 1〜4の結果を出力: foreach{ v -> println(v)}

処理過程

操作の過程を以下の表に示します。

f:id:scache:20180419000703p:plain:w300f:id:scache:20180419000707p:plain:w300

Sequenceの4の列がすべて空白になっています(書き忘れじゃないですよ)。
今回の場合、Sequenceでは値4に対してまったく操作が行われません。ここがListとの大きな違いとなります。

Sequenceでは遅延評価が行われ、値1に対する操作(map -> map -> filter -> take)がすべて終わった後に値2,3の操作が行われます。
13のtake(2)が終わった段階でtake(2)の結果が決定するため値4の処理は行われません。

一方、Listの場合は値14に対してmap(v*v)の操作を行った結果のListを作成します。作成されたListに対してさらにmap(v+1)の操作, さらにfilter(), take()と続きます。

f:id:scache:20180419000652p:plain:w300f:id:scache:20180419000656p:plain:w300

まとめ

Sequence1つの値に対する操作が全て行われた後、必要なら次のが処理される。
Listすべての値に対して1つの操作が行われた後、必要なら次の操作が行われる

※ Sequenceは実際に値が必要になるまで処理が行われない(遅延される)などの違いもあります。

参考

kotlin.sequences - Kotlin Programming Language

文字列中の変数の値を表示するプラグインを作った

プラグインの概要

Kotlinの文字列中の変数が定数値の場合に、その値を文字列中に表示するプラグインです。
↓の例では$tagの表示がtagの値であるtag nameになります。

f:id:scache:20180404100244g:plain  

プラグイン説明

インストール方法

以下の画像のように、IntellijやAndroidStudioの設定から
PluginsBrowse Repositories... をクリックし、ConstStringPlaceHolderで検索することでインストールができます。

f:id:scache:20180404230859p:plain:w300
f:id:scache:20180404230836p:plain:w300

使い方

定数値を表示するには、文字列中の変数にカーソルを移動した後にCollapse(右クリック→FoldingCollapse)をします。

f:id:scache:20180404235225g:plain
f:id:scache:20180404234946g:plain

複数の値を一度に変換する場合は、範囲を選択後にCollapse All(右クリック→FoldingCollapse All)をします。

f:id:scache:20180405001433g:plain

課題

v0.0.1時点では、

  • Javaコードでは使用できない
  • ファイルを開いた時点ではすべてExpandされた状態になる

の課題があるので今後は上記を改善していきたいです。

data classで配列を使う時はequalsをオーバーライドしよう [Kotlin]

data classとは

data classは以下のようにクラスにdataをつけて宣言することで、
コンストラクタで宣言されたプロパティを使ったequals/hashCode/toString関数などを自動生成してくれます。

data class Book(
    val title: String,
    val page: Int
)

val book1 = Book("book", 100)
val book2 = Book("book", 100)
book1.toString()  // => Book(title=book, page=100)
book1 == book2    // => true

toString()で返って来る文字列にプロパティの値が入っていたり、
==(equalsが呼ばれる)を使った比較ができたりします。

data class内で配列を宣言した場合

↓のようにプロパティに配列型を宣言した場合にも、同様にequals()などの関数が自動で生成されます。

data class Book(
    val title: String,
    val author: Array<String>
)
    
val book1 = Book("book", arrayOf("Alice","Bob"))
val book2 = Book("book", arrayOf("Alice","Bob"))
book1.toString() // Book(title=book, author=[Alice, Bob])
book1 == book2   // false

しかし、先ほどとは違いbook1 == book2の結果がfalseになってしまいます。

なぜ配列を使うと比較がうまくできないのか?

配列を使ったdata classをJavaデコンパイルすると、
Array<String>String[]に、data classのequals内の配列の比較はarray1.equals(array2)と同等のコードになります。

Javaの配列型のequals()メソッドは、要素の比較は行わず配列型のインスタンスが同じかどうかを判定するため、配列内の値が等しいだけではtrueになりません。

対処法

equals()メソッドをオーバーライドしてArrays#equlas()を使うことでこの問題を解決することが可能です。

IntellijやAndroidStudioでKotlinPluginを入れている場合(最近のバージョンでは初めから入っています)は、Inspectionが表示され以下のようなequals()/hashCode()を自動生成することが可能になっています。 f:id:scache:20180321002505p:plain

override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (javaClass != other?.javaClass) return false
    other as Book
    if (title != other.title) return false
    if (!Arrays.equals(author, other.author)) return false
    return true
}
override fun hashCode(): Int {
    var result = title.hashCode()
    result = 31 * result + Arrays.hashCode(author)
    return result
}

最後に

data classで配列の比較も良い感じに生成してくれるとありがたいですが、影響がでかいためv2が来るまで変更されることはないと個人的には思います。

Gradle Kotlin DSLを書くための参考になるサイトやページのメモ

Gradle Kotlin DSL

メリット

  • 1回ビルドできる状態にもっていけばあとはコードの補完が効く
  • Groovyを知らなくてもKotlinで書くことができる

デメリット

  • gradle(Groovy版)からKotlinDSLに直すのがつらい
  • あまり使っている人を見ないので思わぬバグを踏む可能性が高い

公式サイト

github.com

Android関係

DroidKaigi2018にいってきた

セッションについて

去年のDroidKaigiでは、MVP, MVVMを使った実装方法、RxJava, Kotlinなどをどう使うか、のようなすぐに役に立つ系の話が多かったイメージがあります。
しかし今年は、なぜそのような設計にしたのか、あるライブラリや技術がどのように実装されているか、という一歩踏み込んだ話が増えた気がします。このような話はすぐには役に立たないかもしれないですが、アプリ開発に限らず良い影響が出ると個人的には思っています。

また、去年はサンプルコードがKotlinの場合は断りを入れている人が多かったですが、今年は当然のようにKotlinやRxJavaが使われていて、技術の移り変わりを感じました。

特によかったセッション

見たセッションはほとんど良かったが、特に良かったもの

  • 詳解 ViewGroupのレイアウト内部実装 by Hiroyuki Seto
  • ConstraintLayout, now and future
    ConstraintLayout完全に理解した(理解したとは言ってない)
    英語は何を言っているかわからなかったが言いたいことは分かった。今年は英語をがんばる

  • AndroidとCPU
    CPU? ARMとか、Snapdragonが強いんでしょ、程度の知識しかなかったがCPUの種類や特徴について知ることができて、想像以上に良いセッションだった。

  • multi-module-no-susume by Shinnosuke Kugimiya
    すごく良く考えて設計しているなと感じたし、設計に銀の弾丸はないなと改めて思った。作るものに適した設計をできる力をつけていきたい。
    次に個人で作るアプリはマルチモジュールやっていく

アフターパーティとか

同級生や前職でお世話になった人、去年のDroidKaigi以来の人と話せて良かった。
去年も評判が良かったコーヒーを飲みたかったが、普段からコーヒーをあまり飲まないので今年も結局飲まずに終わってしまったのが残念。
企業ブースは時間がなくて半分ぐらいしか回れず。

また来年もDroidKaigiがあるのなら発表したいと思う。
DroidKaigi、最高だった。

Kotlinで型パラメータの型情報を取得する

フレームワークやライブラリの仕様で、Classインスタンスが必要になる場合があります。
例: AndroidIntentを作る場合、jsonを特定の型にパースする場合など

JavaとKotlinではこのような場合にどのような違いがあるのか調べました。

Javaの場合

以下のようにAndroidIntentを作る際にActivityClassを渡す場合

public <T> void startActivity(Context context, Class<T> clazz) {
    context.startActivity(new Intent(context, clazz));
}

startActivity(this, MainActivity.class)

上のコードは問題なく動きますがジェネリクスで型が分かっているのに引数でClassを渡しているのは冗長な気がします。 型パラメータからClassを取得しようとするとどうなるのでしょうか

public <T> void startActivity(Context context) {
    context.startActivity(new Intent(context, T.class));
}

これはコンパイルエラーになります。これ以外の構文でもTからClassを取得することはできません。
よってJavaでは型パラメータからClassを取得することは基本的にはできず、引数としてClassを渡さなければいけないのです。

Kotlinの場合

Kotlinの場合ではどうでしょうか?

Kotlinでは多少制約はありますが型パラメータのメソッド呼び出しやフィールドアクセスが可能です。
上のJavaコードをKotlinで書くと以下のようになります。

inline fun <reified T> startActivity(context: Context) {
    context.startActivity(Intent(context, T::class.java))
}

startActivity<MainActivity>(this)

ポイントは

  • inline関数にする必要がある
  • 型パラメータにreifiedをつける
  • 型引数に Nothingや、List<String>のように型パラメータ付きのクラス(Arrayは除く)は使えない。

また、obj is Tのように型チェックも可能です。これもJavaではできないです。

最後に

今回はreifiedの機能や使い方について調べてみました。
reifiedを使うには制約がありますが、それは何故なのかも調べてみたいと思います。